Merge "Add subunit-describe-calls"
diff --git a/HACKING.rst b/HACKING.rst
index 480650c..432db7d 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -160,11 +160,17 @@
Negative Tests
--------------
-TODO: Write the guideline related to negative tests.
+Error handling is an important aspect of API design and usage. Negative
+tests are a way to ensure that an application can gracefully handle
+invalid or unexpected input. However, as a black box integration test
+suite, Tempest is not suitable for handling all negative test cases, as
+the wide variety and complexity of negative tests can lead to long test
+runs and knowledge of internal implementation details. The bulk of
+negative testing should be handled with project function tests. The
+exception to this rule is API tests used for interoperability testing.
Test skips because of Known Bugs
--------------------------------
-
If a test is broken because of a bug it is appropriate to skip the test until
bug has been fixed. You should use the skip_because decorator so that
Tempest's skip tracking tool can watch the bug status.
diff --git a/REVIEWING.rst b/REVIEWING.rst
index bd6018d..676a217 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -72,6 +72,19 @@
scenario tests this is up to the reviewers discretion whether a docstring is
required or not.
+Release Notes
+-------------
+Release notes are how we indicate to users and other consumers of Tempest what
+has changed in a given release. Since Tempest 10.0.0 we've been using `reno`_
+to manage and build the release notes. There are certain types of changes that
+require release notes and we should not approve them without including a release
+note. These include but aren't limited to, any addition, deprecation or removal
+from the lib interface, any change to configuration options (including
+deprecation), CLI additions or deprecations, major feature additions, and
+anything backwards incompatible or would require a user to take note or do
+something extra.
+
+.. _reno: http://docs.openstack.org/developer/reno/
When to approve
---------------
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 7e4503d..eef3620 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -34,7 +34,7 @@
'oslo_config.sphinxconfiggen',
]
-config_generator_config_file = '../../etc/config-generator.tempest.conf'
+config_generator_config_file = '../../tempest/cmd/config-generator.tempest.conf'
sample_config_basename = '_static/tempest'
todo_include_todos = True
@@ -135,7 +135,7 @@
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
-#html_use_smartypants = True
+html_use_smartypants = False
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 32cd3ef..e4b104f 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -26,6 +26,11 @@
- Run tests for admin APIs
- Generate test credentials on the fly (see `Dynamic Credentials`_)
+When keystone uses a policy that requires domain scoped tokens for admin
+actions, the flag ``admin_domain_scope`` must be set to ``True``.
+The admin user configured, if any, must have a role assigned to the domain to
+be usable.
+
Tempest allows for configuring pre-provisioned test credentials as well.
This can be done using the accounts.yaml file (see
`Pre-Provisioned Credentials`_). This file is used to specify an arbitrary
@@ -87,6 +92,14 @@
by dynamic credentials. This option will not have any effect when Tempest is not
configured to use dynamic credentials.
+When the ``admin_domain_scope`` option is set to ``True``, provisioned admin
+accounts will be assigned a role on domain configured in
+``default_credentials_domain_name``. This will make the accounts provisioned
+usable in a cloud where domain scoped tokens are required by keystone for
+admin operations. Note that the the initial pre-provision admin accounts,
+configured in tempest.conf, must have a role on the same domain as well, for
+Dynamic Credentials to work.
+
Pre-Provisioned Credentials
"""""""""""""""""""""""""""
@@ -124,6 +137,18 @@
to the tests using the credentials, and failure to do this will likely cause
unexpected failures in some tests.
+When the keystone in the target cloud requires domain scoped tokens to
+perform admin actions, all pre-provisioned admin users must have a role
+assigned on the domain where test accounts a provisioned.
+The option ``admin_domain_scope`` is used to tell tempest that domain scoped
+tokens shall be used. ``default_credentials_domain_name`` is the domain where
+test accounts are expected to be provisioned if no domain is specified.
+
+Note that if credentials are pre-provisioned via ``tempest account-generator``
+the role on the domain will be assigned automatically for you, as long as
+``admin_domain_scope`` as ``default_credentials_domain_name`` are configured
+properly in tempest.conf.
+
Pre-Provisioned Credentials are also know as accounts.yaml or accounts file.
Compute
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 1faf711..60ff46b 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -14,6 +14,7 @@
plugin-registry
library
microversion_testing
+ test-removal
------------
Field Guides
@@ -53,6 +54,7 @@
javelin
subunit_describe_calls
workspace
+ run
==================
Indices and tables
diff --git a/doc/source/library.rst b/doc/source/library.rst
index a89512c..6a2fb83 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -66,3 +66,4 @@
library/rest_client
library/utils
library/api_microversion_testing
+ library/auth
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 3568470..17059e4 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -19,6 +19,7 @@
multiple Microversion tests in a single Tempest operation, configuration
options should represent the range of test target Microversions.
New configuration options are:
+
* min_microversion
* max_microversion
@@ -130,8 +131,9 @@
If that range is out of configured Microversion range then, test
will be skipped.
-*NOTE: Microversion testing is supported at test class level not at individual
-test case level.*
+.. note:: Microversion testing is supported at test class level not at
+ individual test case level.
+
For example:
Below test is applicable for Microversion from 2.2 till 2.9::
@@ -211,3 +213,7 @@
* `2.10`_
.. _2.10: http://docs.openstack.org/developer/nova/api_microversion_history.html#id9
+
+ * `2.20`_
+
+ .. _2.20: http://docs.openstack.org/developer/nova/api_microversion_history.html#id18
diff --git a/doc/source/run.rst b/doc/source/run.rst
new file mode 100644
index 0000000..07fa5f7
--- /dev/null
+++ b/doc/source/run.rst
@@ -0,0 +1,5 @@
+-----------
+Tempest Run
+-----------
+
+.. automodule:: tempest.cmd.run
diff --git a/doc/source/test-removal.rst b/doc/source/test-removal.rst
new file mode 100644
index 0000000..6570bb7
--- /dev/null
+++ b/doc/source/test-removal.rst
@@ -0,0 +1,168 @@
+Tempest Test Removal Procedure
+==============================
+
+Historically tempest was the only way of doing functional testing and
+integration testing in OpenStack. This was mostly only an artifact of tempest
+being the only proven pattern for doing this, not an artifact of a design
+decision. However, moving forward as functional testing is being spun up in
+each individual project we really only want tempest to be the integration test
+suite it was intended to be; testing the high level interactions between
+projects through REST API requests. In this model there are probably existing
+tests that aren't the best fit living in tempest. However, since tempest is
+largely still the only gating test suite in this space we can't carelessly rip
+out everything from the tree. This document outlines the procedure which was
+developed to ensure we minimize the risk for removing something of value from
+the tempest tree.
+
+This procedure might seem overly conservative and slow paced, but this is by
+design to try and ensure we don't remove something that is actually providing
+value. Having potential duplication between testing is not a big deal
+especially compared to the alternative of removing something which is actually
+providing value and is actively catching bugs, or blocking incorrect patches
+from landing.
+
+Proposing a test removal
+------------------------
+
+3 prong rule for removal
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+In the proposal etherpad we'll be looking for answers to 3 questions
+
+ #. The tests proposed for removal must have equiv. coverage in a different
+ project's test suite (whether this is another gating test project, or an in
+ tree funcitonal test suite) For API tests preferably the other project will
+ have a similar source of friction in place to prevent breaking api changes
+ so that we don't regress and let breaking api changes slip through the
+ gate.
+ #. The test proposed for removal has a failure rate < 0.50% in the gate over
+ the past release (the value and interval will likely be adjusted in the
+ future)
+ #. There must not be an external user/consumer of tempest that depends on the
+ test proposed for removal
+
+The answers to 1 and 2 are easy to verify. For 1 just provide a link to the new
+test location. If you are linking to the tempest removal patch please also put
+a Depends-On in the commit message for the commit which moved the test into
+another repo.
+
+For prong 2 you can use OpenStack-Health:
+
+Using OpenStack-Health
+""""""""""""""""""""""
+
+Go to: http://status.openstack.org/openstack-health and then navigate to a per
+test page for six months. You'll end up with a page that will graph the success
+and failure rates on the bottom graph. For example, something like `this URL`_.
+
+.. _this URL: http://status.openstack.org/openstack-health/#/test/tempest.scenario.test_volume_boot_pattern.TestVolumeBootPatternV2.test_volume_boot_pattern?groupKey=project&resolutionKey=day&duration=P6M
+
+The Old Way using subunit2sql directly
+""""""""""""""""""""""""""""""""""""""
+
+SELECT * from tests where test_id like "%test_id%";
+(where $test_id is the full test_id, but truncated to the class because of
+setupclass or teardownclass failures)
+
+You can access the infra mysql subunit2sql db w/ read-only permissions with:
+
+ * hostname: logstash.openstack.org
+ * username: query
+ * password: query
+ * db_name: subunit2sql
+
+For example if you were trying to remove the test with the id:
+tempest.api.compute.admin.test_flavors_negative.FlavorsAdminNegativeTestJSON.test_get_flavor_details_for_deleted_flavor
+you would run the following:
+
+ #. run: "mysql -u query -p -h logstash.openstack.org subunit2sql" to connect
+ to the subunit2sql db
+ #. run the query: MySQL [subunit2sql]> select * from tests where test_id like
+ "tempest.api.compute.admin.test_flavors_negative.FlavorsAdminNegativeTestJSON%";
+ which will return a table of all the tests in the class (but it will also
+ catch failures in setupClass and tearDownClass)
+ #. paste the output table with numbers and the mysql command you ran to
+ generate it into the etherpad.
+
+Eventually a cli interface will be created to make that a bit more friendly.
+Also a dashboard is in the works so we don't need to manually run the command.
+
+The intent of the 2nd prong is to verify that moving the test into a project
+specific testing is preventing bugs (assuming the tempest tests were catching
+issues) from bubbling up a layer into tempest jobs. If we're seeing failure
+rates above a certain threshold in the gate checks that means the functional
+testing isn't really being effective in catching that bug (and therefore
+blocking it from landing) and having the testing run in tempest still has
+value.
+
+However for the 3rd prong verification is a bit more subjective. The original
+intent of this prong was mostly for refstack/defcore and also for things that
+running on the stable branches. We don't want to remove any tests if that
+would break our api consistency checking between releases, or something that
+defcore/refstack is depending on being in tempest. It's worth pointing out
+that if a test is used in defcore as part of interop testing then it will
+probably have continuing value being in tempest as part of the
+integration/integrated tests in general. This is one area where some overlap
+is expected between testing in projects and tempest, which is not a bad thing.
+
+Discussing the 3rd prong
+""""""""""""""""""""""""
+
+There are 2 approaches to addressing the 3rd prong. Either it can be raised
+during a qa meeting during the tempest discussion. Please put it on the agenda
+well ahead of the scheduled meeting. Since the meeting time will be well known
+ahead of time anyone who depends on the tests will have ample time beforehand
+to outline any concerns on the before the meeting. To give ample time for
+people to respond to removal proposals please add things to the agend by the
+Monday before the meeting.
+
+The other option is to raise the removal on the openstack-dev mailing list.
+(for example see: http://lists.openstack.org/pipermail/openstack-dev/2016-February/086218.html )
+This will raise the issue to the wider community and attract at least the same
+(most likely more) attention than discussing it during the irc meeting. The
+only downside is that it might take more time to get a response, given the
+nature of ML.
+
+Exceptions to this procedure
+----------------------------
+
+For the most part all tempest test removals have to go through this procedure
+there are a couple of exceptions though:
+
+ #. The class of testing has been decided to be outside the scope of tempest.
+ #. A revert for a patch which added a broken test, or testing which didn't
+ actually run in the gate (basically any revert for something which
+ shouldn't have been added)
+
+For the first exception type the only types of testing in tree which have been
+declared out of scope at this point are:
+
+ * The CLI tests (which should be completely removed at this point)
+ * Neutron Adv. Services testing (which should be completely removed at this
+ point)
+ * XML API Tests (which should be completely removed at this point)
+ * EC2 API/boto tests (which should be completely removed at this point)
+
+For tests that fit into this category the only criteria for removal is that
+there is equivalent testing elsewhere.
+
+Tempest Scope
+^^^^^^^^^^^^^
+
+Also starting in the liberty cycle tempest has defined a set of projects which
+are defined as in scope for direct testing in tempest. As of today that list
+is:
+
+ * Keystone
+ * Nova
+ * Glance
+ * Cinder
+ * Neutron
+ * Swift
+
+anything that lives in tempest which doesn't test one of these projects can be
+removed assuming there is equivalent testing elsewhere. Preferably using the
+`tempest plugin mechanism`_
+to mantain continuity after migrating the tests out of tempest
+
+.. _tempest plugin mechanism: http://docs.openstack.org/developer/tempest/plugin.html
diff --git a/releasenotes/notes/add-network-versions-client-d90e8334e1443f5c.yaml b/releasenotes/notes/add-network-versions-client-d90e8334e1443f5c.yaml
new file mode 100644
index 0000000..07e3151
--- /dev/null
+++ b/releasenotes/notes/add-network-versions-client-d90e8334e1443f5c.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - Adds a network version client for querying
+ Neutron's API version discovery URL ("GET /").
diff --git a/releasenotes/notes/add-tempest-run-3d0aaf69c2ca4115.yaml b/releasenotes/notes/add-tempest-run-3d0aaf69c2ca4115.yaml
new file mode 100644
index 0000000..429bf52
--- /dev/null
+++ b/releasenotes/notes/add-tempest-run-3d0aaf69c2ca4115.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - Adds the tempest run command to the unified tempest CLI. This new command
+ is used for running tempest tests.
diff --git a/releasenotes/releasenotes/notes/add-tempest-workspaces-228a2ba4690b5589.yaml b/releasenotes/notes/add-tempest-workspaces-228a2ba4690b5589.yaml
similarity index 100%
rename from releasenotes/releasenotes/notes/add-tempest-workspaces-228a2ba4690b5589.yaml
rename to releasenotes/notes/add-tempest-workspaces-228a2ba4690b5589.yaml
diff --git a/releasenotes/notes/bug-1486834-7ebca15836ae27a9.yaml b/releasenotes/notes/bug-1486834-7ebca15836ae27a9.yaml
new file mode 100644
index 0000000..b2190f3
--- /dev/null
+++ b/releasenotes/notes/bug-1486834-7ebca15836ae27a9.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Tempest library auth interface now supports
+ filtering with catalog name. Note that filtering by
+ name is only successful if a known service type is
+ provided.
diff --git a/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml b/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml
new file mode 100644
index 0000000..b7850d0
--- /dev/null
+++ b/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ Define identity service clients as libraries
+ The following identity service clients are defined as library interface,
+ so the other projects can use these modules as stable libraries without
+ any maintenance changes.
+
+ * endpoints_client(v2)
diff --git a/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml b/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
new file mode 100644
index 0000000..90a5056
--- /dev/null
+++ b/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
@@ -0,0 +1,14 @@
+---
+features:
+ - |
+ Define image service clients as libraries
+ The following image service clients are defined as library interface,
+ so the other projects can use these modules as stable libraries
+ without any maintenance changes.
+
+ * image_members_client(v1)
+ * image_members_client(v2)
+ * images_client(v2)
+ * namespaces_client(v2)
+ * resource_types_client(v2)
+ * schemas_client(v2)
diff --git a/releasenotes/notes/remove-input-scenarios-functionality-01308e6d4307f580.yaml b/releasenotes/notes/remove-input-scenarios-functionality-01308e6d4307f580.yaml
new file mode 100644
index 0000000..4ee883f
--- /dev/null
+++ b/releasenotes/notes/remove-input-scenarios-functionality-01308e6d4307f580.yaml
@@ -0,0 +1,11 @@
+---
+upgrade:
+ - The input scenarios functionality no longer exists in tempest. This caused
+ a large number of issues for limited benefit and was only used by a single
+ test, test_server_basic_ops. If you were using this functionality you'll
+ now have to do it manually with a script and/or tempest workspaces
+deprecations:
+ - All the options in the input-scenario group are now deprecated. These were
+ only used in tree by the now removed input scenarios functionality in
+ test_server_basic_ops. They were only deprecated because there could be
+ external consumers via plugins. They will be removed during the Ocata cycle.
diff --git a/releasenotes/notes/tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml b/releasenotes/notes/tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
new file mode 100644
index 0000000..eeda921
--- /dev/null
+++ b/releasenotes/notes/tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
@@ -0,0 +1,12 @@
+---
+upgrade:
+ - The location on disk that the *tempest init* command looks for has changed.
+ Previously it would attempt to use python packaging's data files to guess
+ where setuptools/distutils were installing data files, which was incredibly
+ unreliable and depended on how you installed tempest and which versions of
+ setuptools, distutils, and python you had installed. Instead, now it will
+ use either /etc/tempest, $XDG_CONFIG_PATH/.config/tempest, or
+ ~/.tempest/etc (attempted in that order). If none of these exist it will
+ create an empty ~/.tempest/etc directory. If you were relying on the
+ previous behavior and none of these directories were being used you will
+ need to move the files to live in one of these directories.
diff --git a/setup.cfg b/setup.cfg
index 56344b0..2a3000d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -43,6 +43,7 @@
list-plugins = tempest.cmd.list_plugins:TempestListPlugins
verify-config = tempest.cmd.verify_tempest_config:TempestVerifyConfig
workspace = tempest.cmd.workspace:TempestWorkspace
+ run = tempest.cmd.run:TempestRun
oslo.config.opts =
tempest.config = tempest.config:list_opts
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 25efd2e..84b00a7 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.compute import base
from tempest.common import tempest_fixtures as fixtures
from tempest.common.utils import data_utils
@@ -36,10 +38,17 @@
cls.aggregate_name_prefix = 'test_aggregate'
cls.az_name_prefix = 'test_az'
- hosts_all = cls.os_adm.hosts_client.list_hosts()['hosts']
- hosts = map(lambda x: x['host_name'],
- filter(lambda y: y['service'] == 'compute', hosts_all))
- cls.host = hosts[0]
+ cls.host = None
+ hypers = cls.os_adm.hypervisor_client.list_hypervisors(
+ detail=True)['hypervisors']
+ hosts_available = [hyper['service']['host'] for hyper in hypers
+ if (hyper['state'] == 'up' and
+ hyper['status'] == 'enabled')]
+ if hosts_available:
+ cls.host = hosts_available[0]
+ else:
+ raise testtools.TestCase.failureException(
+ "no available compute node found")
@test.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
def test_aggregate_create_delete(self):
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 07423ff..da9d548 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -136,7 +136,10 @@
self.validation_resources['keypair']['private_key'],
server=self.server,
servers_client=self.client)
- self.assertTrue(linux_client.hostname_equals_servername(self.name))
+ hostname = linux_client.get_hostname()
+ msg = ('Failed while verifying servername equals hostname. Expected '
+ 'hostname "%s" but got "%s".' % (self.name, hostname))
+ self.assertEqual(self.name, hostname, msg)
@test.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
def test_create_server_with_scheduler_hint_group(self):
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index b18789e..357c907 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -109,9 +109,12 @@
@test.attr(type=['negative'])
@test.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
+ full_list = self.client.list_servers()['servers']
# List servers by specifying a greater value for limit
- body = self.client.list_servers(limit=100)
- self.assertEqual(len(self.existing_fixtures), len(body['servers']))
+ limit = len(full_list) + 100
+ body = self.client.list_servers(limit=limit)
+ self.assertEqual(len(full_list), len(body['servers']))
@test.attr(type=['negative'])
@test.idempotent_id('679bc053-5e70-4514-9800-3dfab1a380a6')
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 3a4428a..0d06119 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -375,17 +375,19 @@
'backup_type': "daily",
'instance_uuid': self.server_id,
}
+ params = {
+ 'status': 'active',
+ 'sort_key': 'created_at',
+ 'sort_dir': 'asc'
+ }
if CONF.image_feature_enabled.api_v1:
- params = dict(
- properties=properties, status='active',
- sort_key='created_at', sort_dir='asc',)
+ for key, value in properties.items():
+ params['property-%s' % key] = value
image_list = glance_client.list_images(
detail=True,
**params)['images']
else:
# Additional properties are flattened in glance v2.
- params = dict(
- status='active', sort_key='created_at', sort_dir='asc')
params.update(properties)
image_list = glance_client.list_images(params)['images']
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 32faf7d..9834d02 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -61,9 +61,6 @@
waiters.wait_for_server_status(cls.servers_client, cls.server_id,
'ACTIVE')
- def setUp(self):
- super(ServerRescueTestJSON, self).setUp()
-
@classmethod
def resource_cleanup(cls):
# Deleting the floating IP which is created in this method
@@ -72,9 +69,6 @@
cls.sg_id)
super(ServerRescueTestJSON, cls).resource_cleanup()
- def tearDown(self):
- super(ServerRescueTestJSON, self).tearDown()
-
def _unrescue(self, server_id):
self.servers_client.unrescue_server(server_id)
waiters.wait_for_server_status(self.servers_client, server_id,
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index fa3fdfe..05c23ee 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -200,7 +200,7 @@
server=self.server,
servers_client=self.servers_client)
- command = 'grep vd /proc/partitions | wc -l'
+ command = 'grep [vs]d /proc/partitions | wc -l'
nb_partitions = linux_client.exec_command(command).strip()
self.assertEqual(number_of_partition, nb_partitions)
diff --git a/tempest/api/data_processing/test_node_group_templates.py b/tempest/api/data_processing/test_node_group_templates.py
index 388bb58..c2dae85 100644
--- a/tempest/api/data_processing/test_node_group_templates.py
+++ b/tempest/api/data_processing/test_node_group_templates.py
@@ -25,10 +25,6 @@
if cls.default_plugin is None:
raise cls.skipException("No Sahara plugins configured")
- @classmethod
- def resource_setup(cls):
- super(NodeGroupTemplateTest, cls).resource_setup()
-
def _create_node_group_template(self, template_name=None):
"""Creates Node Group Template with optional name specified.
diff --git a/tempest/api/identity/admin/v2/test_endpoints.py b/tempest/api/identity/admin/v2/test_endpoints.py
index df75d0a..651a316 100644
--- a/tempest/api/identity/admin/v2/test_endpoints.py
+++ b/tempest/api/identity/admin/v2/test_endpoints.py
@@ -28,7 +28,8 @@
s_type = data_utils.rand_name('type')
s_description = data_utils.rand_name('description')
cls.service_data = cls.services_client.create_service(
- s_name, s_type, description=s_description)['OS-KSADM:service']
+ name=s_name, type=s_type,
+ description=s_description)['OS-KSADM:service']
cls.service_id = cls.service_data['id']
cls.service_ids.append(cls.service_id)
# Create endpoints so as to use for LIST and GET test cases
@@ -37,8 +38,8 @@
region = data_utils.rand_name('region')
url = data_utils.rand_url()
endpoint = cls.endpoints_client.create_endpoint(
- cls.service_id,
- region,
+ service_id=cls.service_id,
+ region=region,
publicurl=url,
adminurl=url,
internalurl=url)['endpoint']
@@ -70,8 +71,8 @@
region = data_utils.rand_name('region')
url = data_utils.rand_url()
endpoint = self.endpoints_client.create_endpoint(
- self.service_id,
- region,
+ service_id=self.service_id,
+ region=region,
publicurl=url,
adminurl=url,
internalurl=url)['endpoint']
diff --git a/tempest/api/identity/admin/v2/test_roles.py b/tempest/api/identity/admin/v2/test_roles.py
index 5847129..0924619 100644
--- a/tempest/api/identity/admin/v2/test_roles.py
+++ b/tempest/api/identity/admin/v2/test_roles.py
@@ -84,28 +84,30 @@
def test_assign_user_role(self):
"""Assign a role to a user on a tenant."""
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'], user['id'],
- role['id'])
- roles = self.roles_client.list_user_roles(tenant['id'],
- user['id'])['roles']
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
+ roles = self.roles_client.list_user_roles_on_project(
+ tenant['id'], user['id'])['roles']
self.assert_role_in_role_list(role, roles)
@test.idempotent_id('f0b9292c-d3ba-4082-aa6c-440489beef69')
def test_remove_user_role(self):
"""Remove a role assigned to a user on a tenant."""
(user, tenant, role) = self._get_role_params()
- user_role = self.roles_client.assign_user_role(tenant['id'],
- user['id'],
- role['id'])['role']
- self.roles_client.delete_user_role(tenant['id'], user['id'],
- user_role['id'])
+ user_role = self.roles_client.create_user_role_on_project(
+ tenant['id'], user['id'], role['id'])['role']
+ self.roles_client.delete_role_from_user_on_project(tenant['id'],
+ user['id'],
+ user_role['id'])
@test.idempotent_id('262e1e3e-ed71-4edd-a0e5-d64e83d66d05')
def test_list_user_roles(self):
"""List roles assigned to a user on tenant."""
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'], user['id'],
- role['id'])
- roles = self.roles_client.list_user_roles(tenant['id'],
- user['id'])['roles']
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
+ roles = self.roles_client.list_user_roles_on_project(
+ tenant['id'], user['id'])['roles']
self.assert_role_in_role_list(role, roles)
diff --git a/tempest/api/identity/admin/v2/test_roles_negative.py b/tempest/api/identity/admin/v2/test_roles_negative.py
index fd56285..770bb14 100644
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ b/tempest/api/identity/admin/v2/test_roles_negative.py
@@ -123,9 +123,10 @@
# Non-administrator user should not be authorized to
# assign a role to user
(user, tenant, role) = self._get_role_params()
- self.assertRaises(lib_exc.Forbidden,
- self.non_admin_roles_client.assign_user_role,
- tenant['id'], user['id'], role['id'])
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.non_admin_roles_client.create_user_role_on_project,
+ tenant['id'], user['id'], role['id'])
@test.attr(type=['negative'])
@test.idempotent_id('f0d2683c-5603-4aee-95d7-21420e87cfd8')
@@ -134,9 +135,10 @@
(user, tenant, role) = self._get_role_params()
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
- self.assertRaises(lib_exc.Unauthorized,
- self.roles_client.assign_user_role, tenant['id'],
- user['id'], role['id'])
+ self.assertRaises(
+ lib_exc.Unauthorized,
+ self.roles_client.create_user_role_on_project, tenant['id'],
+ user['id'], role['id'])
self.client.auth_provider.clear_auth()
@test.attr(type=['negative'])
@@ -145,7 +147,8 @@
# Attempt to assign a non existent role to user should fail
(user, tenant, role) = self._get_role_params()
non_existent_role = data_utils.rand_uuid_hex()
- self.assertRaises(lib_exc.NotFound, self.roles_client.assign_user_role,
+ self.assertRaises(lib_exc.NotFound,
+ self.roles_client.create_user_role_on_project,
tenant['id'], user['id'], non_existent_role)
@test.attr(type=['negative'])
@@ -154,7 +157,8 @@
# Attempt to assign a role on a non existent tenant should fail
(user, tenant, role) = self._get_role_params()
non_existent_tenant = data_utils.rand_uuid_hex()
- self.assertRaises(lib_exc.NotFound, self.roles_client.assign_user_role,
+ self.assertRaises(lib_exc.NotFound,
+ self.roles_client.create_user_role_on_project,
non_existent_tenant, user['id'], role['id'])
@test.attr(type=['negative'])
@@ -162,9 +166,11 @@
def test_assign_duplicate_user_role(self):
# Duplicate user role should not get assigned
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'], user['id'],
- role['id'])
- self.assertRaises(lib_exc.Conflict, self.roles_client.assign_user_role,
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
+ self.assertRaises(lib_exc.Conflict,
+ self.roles_client.create_user_role_on_project,
tenant['id'], user['id'], role['id'])
@test.attr(type=['negative'])
@@ -173,26 +179,27 @@
# Non-administrator user should not be authorized to
# remove a user's role
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'],
- user['id'],
- role['id'])
- self.assertRaises(lib_exc.Forbidden,
- self.non_admin_roles_client.delete_user_role,
- tenant['id'], user['id'], role['id'])
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.non_admin_roles_client.delete_role_from_user_on_project,
+ tenant['id'], user['id'], role['id'])
@test.attr(type=['negative'])
@test.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
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'],
- user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(lib_exc.Unauthorized,
- self.roles_client.delete_user_role, tenant['id'],
- user['id'], role['id'])
+ self.roles_client.delete_role_from_user_on_project,
+ tenant['id'], user['id'], role['id'])
self.client.auth_provider.clear_auth()
@test.attr(type=['negative'])
@@ -200,11 +207,12 @@
def test_remove_user_role_non_existent_role(self):
# Attempt to delete a non existent role from a user should fail
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'],
- user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
non_existent_role = data_utils.rand_uuid_hex()
- self.assertRaises(lib_exc.NotFound, self.roles_client.delete_user_role,
+ self.assertRaises(lib_exc.NotFound,
+ self.roles_client.delete_role_from_user_on_project,
tenant['id'], user['id'], non_existent_role)
@test.attr(type=['negative'])
@@ -212,11 +220,12 @@
def test_remove_user_role_non_existent_tenant(self):
# Attempt to remove a role from a non existent tenant should fail
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'],
- user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
non_existent_tenant = data_utils.rand_uuid_hex()
- self.assertRaises(lib_exc.NotFound, self.roles_client.delete_user_role,
+ self.assertRaises(lib_exc.NotFound,
+ self.roles_client.delete_role_from_user_on_project,
non_existent_tenant, user['id'], role['id'])
@test.attr(type=['negative'])
@@ -225,11 +234,13 @@
# Non-administrator user should not be authorized to list
# a user's roles
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'], user['id'],
- role['id'])
- self.assertRaises(lib_exc.Forbidden,
- self.non_admin_roles_client.list_user_roles,
- tenant['id'], user['id'])
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.non_admin_roles_client.list_user_roles_on_project,
+ tenant['id'], user['id'])
@test.attr(type=['negative'])
@test.idempotent_id('682adfb2-fd5f-4b0a-a9ca-322e9bebb907')
@@ -240,7 +251,8 @@
self.client.delete_token(token)
try:
self.assertRaises(lib_exc.Unauthorized,
- self.roles_client.list_user_roles, tenant['id'],
+ self.roles_client.list_user_roles_on_project,
+ tenant['id'],
user['id'])
finally:
self.client.auth_provider.clear_auth()
diff --git a/tempest/api/identity/admin/v2/test_services.py b/tempest/api/identity/admin/v2/test_services.py
index fe83759..6c9b564 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -35,10 +35,11 @@
# GET Service
# Creating a Service
name = data_utils.rand_name('service')
- type = data_utils.rand_name('type')
+ s_type = data_utils.rand_name('type')
description = data_utils.rand_name('description')
service_data = self.services_client.create_service(
- name, type, description=description)['OS-KSADM:service']
+ name=name, type=s_type,
+ description=description)['OS-KSADM:service']
self.assertFalse(service_data['id'] is None)
self.addCleanup(self._del_service, service_data['id'])
# Verifying response body of create service
@@ -46,7 +47,7 @@
self.assertIn('name', service_data)
self.assertEqual(name, service_data['name'])
self.assertIn('type', service_data)
- self.assertEqual(type, service_data['type'])
+ self.assertEqual(s_type, service_data['type'])
self.assertIn('description', service_data)
self.assertEqual(description, service_data['description'])
# Get service
@@ -68,15 +69,15 @@
def test_create_service_without_description(self):
# Create a service only with name and type
name = data_utils.rand_name('service')
- type = data_utils.rand_name('type')
- service = self.services_client.create_service(name,
- type)['OS-KSADM:service']
+ s_type = data_utils.rand_name('type')
+ service = self.services_client.create_service(
+ name=name, type=s_type)['OS-KSADM:service']
self.assertIn('id', service)
self.addCleanup(self._del_service, service['id'])
self.assertIn('name', service)
self.assertEqual(name, service['name'])
self.assertIn('type', service)
- self.assertEqual(type, service['type'])
+ self.assertEqual(s_type, service['type'])
@test.attr(type='smoke')
@test.idempotent_id('34ea6489-012d-4a86-9038-1287cadd5eca')
@@ -85,10 +86,12 @@
services = []
for _ in moves.xrange(3):
name = data_utils.rand_name('service')
- type = data_utils.rand_name('type')
+ s_type = data_utils.rand_name('type')
description = data_utils.rand_name('description')
+
service = self.services_client.create_service(
- name, type, description=description)['OS-KSADM:service']
+ name=name, type=s_type,
+ description=description)['OS-KSADM:service']
services.append(service)
service_ids = map(lambda x: x['id'], services)
diff --git a/tempest/api/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
index 5169dae..d1e0217 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -43,7 +43,7 @@
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(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
self.data.tenants.append(tenant)
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.delete_tenant,
@@ -54,7 +54,7 @@
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(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
self.data.tenants.append(tenant)
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
@@ -75,7 +75,7 @@
def test_tenant_create_duplicate(self):
# Tenant names should be unique
tenant_name = data_utils.rand_name(name='tenant')
- body = self.tenants_client.create_tenant(tenant_name)['tenant']
+ body = self.tenants_client.create_tenant(name=tenant_name)['tenant']
tenant = body
self.data.tenants.append(tenant)
tenant1_id = body.get('id')
@@ -83,7 +83,7 @@
self.addCleanup(self.tenants_client.delete_tenant, tenant1_id)
self.addCleanup(self.data.tenants.remove, tenant)
self.assertRaises(lib_exc.Conflict, self.tenants_client.create_tenant,
- tenant_name)
+ name=tenant_name)
@test.attr(type=['negative'])
@test.idempotent_id('d26b278a-6389-4702-8d6e-5980d80137e0')
@@ -92,7 +92,7 @@
tenant_name = data_utils.rand_name(name='tenant')
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.create_tenant,
- tenant_name)
+ name=tenant_name)
@test.attr(type=['negative'])
@test.idempotent_id('a3ee9d7e-6920-4dd5-9321-d4b2b7f0a638')
@@ -103,7 +103,7 @@
self.client.delete_token(token)
self.assertRaises(lib_exc.Unauthorized,
self.tenants_client.create_tenant,
- tenant_name)
+ name=tenant_name)
self.client.auth_provider.clear_auth()
@test.attr(type=['negative'])
@@ -121,7 +121,7 @@
tenant_name = 'a' * 65
self.assertRaises(lib_exc.BadRequest,
self.tenants_client.create_tenant,
- tenant_name)
+ name=tenant_name)
@test.attr(type=['negative'])
@test.idempotent_id('bd20dc2a-9557-4db7-b755-f48d952ad706')
@@ -135,7 +135,7 @@
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(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
self.data.tenants.append(tenant)
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.update_tenant,
@@ -146,7 +146,7 @@
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(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
self.data.tenants.append(tenant)
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index 8d0b9b1..1aa80df 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -28,7 +28,8 @@
tenants = []
for _ in moves.xrange(3):
tenant_name = data_utils.rand_name(name='tenant-new')
- tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(
+ name=tenant_name)['tenant']
self.data.tenants.append(tenant)
tenants.append(tenant)
tenant_ids = map(lambda x: x['id'], tenants)
@@ -49,7 +50,7 @@
# 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(tenant_name,
+ body = self.tenants_client.create_tenant(name=tenant_name,
description=tenant_desc)
tenant = body['tenant']
self.data.tenants.append(tenant)
@@ -68,7 +69,8 @@
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(tenant_name, enabled=True)
+ body = self.tenants_client.create_tenant(name=tenant_name,
+ enabled=True)
tenant = body['tenant']
self.data.tenants.append(tenant)
tenant_id = tenant['id']
@@ -84,7 +86,8 @@
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(tenant_name, enabled=False)
+ body = self.tenants_client.create_tenant(name=tenant_name,
+ enabled=False)
tenant = body['tenant']
self.data.tenants.append(tenant)
tenant_id = tenant['id']
@@ -102,7 +105,7 @@
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(t_name1)['tenant']
+ body = self.tenants_client.create_tenant(name=t_name1)['tenant']
tenant = body
self.data.tenants.append(tenant)
@@ -129,7 +132,8 @@
# 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(t_name, description=t_desc)
+ body = self.tenants_client.create_tenant(name=t_name,
+ description=t_desc)
tenant = body['tenant']
self.data.tenants.append(tenant)
@@ -157,7 +161,7 @@
# Update the enabled attribute of a tenant
t_name = data_utils.rand_name(name='tenant')
t_en = False
- body = self.tenants_client.create_tenant(t_name, enabled=t_en)
+ body = self.tenants_client.create_tenant(name=t_name, enabled=t_en)
tenant = body['tenant']
self.data.tenants.append(tenant)
diff --git a/tempest/api/identity/admin/v2/test_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
index ee04420..5cf337b 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -27,11 +27,13 @@
user_password = data_utils.rand_password()
# first:create a tenant
tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
self.data.tenants.append(tenant)
# second:create a user
- user = self.users_client.create_user(user_name, user_password,
- tenant['id'], '')['user']
+ user = self.users_client.create_user(name=user_name,
+ password=user_password,
+ tenantId=tenant['id'],
+ email='')['user']
self.data.users.append(user)
# then get a token for the user
body = self.token_client.auth(user_name,
@@ -62,17 +64,21 @@
user_password = data_utils.rand_password()
tenant_id = None # No default tenant so will get unscoped token.
email = ''
- user = self.users_client.create_user(user_name, user_password,
- tenant_id, email)['user']
+ user = self.users_client.create_user(name=user_name,
+ password=user_password,
+ tenantId=tenant_id,
+ email=email)['user']
self.data.users.append(user)
# Create a couple tenants.
tenant1_name = data_utils.rand_name(name='tenant')
- tenant1 = self.tenants_client.create_tenant(tenant1_name)['tenant']
+ tenant1 = self.tenants_client.create_tenant(
+ name=tenant1_name)['tenant']
self.data.tenants.append(tenant1)
tenant2_name = data_utils.rand_name(name='tenant')
- tenant2 = self.tenants_client.create_tenant(tenant2_name)['tenant']
+ tenant2 = self.tenants_client.create_tenant(
+ name=tenant2_name)['tenant']
self.data.tenants.append(tenant2)
# Create a role
@@ -81,11 +87,13 @@
self.data.roles.append(role)
# Grant the user the role on the tenants.
- self.roles_client.assign_user_role(tenant1['id'], user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(tenant1['id'],
+ user['id'],
+ role['id'])
- self.roles_client.assign_user_role(tenant2['id'], user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(tenant2['id'],
+ user['id'],
+ role['id'])
# Get an unscoped token.
body = self.token_client.auth(user_name, user_password)
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index d860d2f..167cbc7 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -36,9 +36,10 @@
def test_create_user(self):
# Create a user
self.data.setup_test_tenant()
- user = self.users_client.create_user(self.alt_user, self.alt_password,
- self.data.tenant['id'],
- self.alt_email)['user']
+ user = self.users_client.create_user(name=self.alt_user,
+ password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)['user']
self.data.users.append(user)
self.assertEqual(self.alt_user, user['name'])
@@ -47,9 +48,10 @@
# Create a user with enabled : False
self.data.setup_test_tenant()
name = data_utils.rand_name('test_user')
- user = self.users_client.create_user(name, self.alt_password,
- self.data.tenant['id'],
- self.alt_email,
+ user = self.users_client.create_user(name=name,
+ password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email,
enabled=False)['user']
self.data.users.append(user)
self.assertEqual(name, user['name'])
@@ -61,9 +63,10 @@
# Test case to check if updating of user attributes is successful.
test_user = data_utils.rand_name('test_user')
self.data.setup_test_tenant()
- user = self.users_client.create_user(test_user, self.alt_password,
- self.data.tenant['id'],
- self.alt_email)['user']
+ user = self.users_client.create_user(name=test_user,
+ password=self.alt_password,
+ tenantId=self.data.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'])
# Updating user details with new values
@@ -87,9 +90,10 @@
# Delete a user
test_user = data_utils.rand_name('test_user')
self.data.setup_test_tenant()
- user = self.users_client.create_user(test_user, self.alt_password,
- self.data.tenant['id'],
- self.alt_email)['user']
+ user = self.users_client.create_user(name=test_user,
+ password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)['user']
self.users_client.delete_user(user['id'])
@test.idempotent_id('aca696c3-d645-4f45-b728-63646045beb1')
@@ -139,16 +143,18 @@
fetched_user_ids = list()
password1 = data_utils.rand_password()
alt_tenant_user1 = data_utils.rand_name('tenant_user1')
- user1 = self.users_client.create_user(alt_tenant_user1, password1,
- self.data.tenant['id'],
- 'user1@123')['user']
+ user1 = self.users_client.create_user(name=alt_tenant_user1,
+ password=password1,
+ tenantId=self.data.tenant['id'],
+ email='user1@123')['user']
user_ids.append(user1['id'])
self.data.users.append(user1)
password2 = data_utils.rand_password()
alt_tenant_user2 = data_utils.rand_name('tenant_user2')
- user2 = self.users_client.create_user(alt_tenant_user2, password2,
- self.data.tenant['id'],
- 'user2@123')['user']
+ user2 = self.users_client.create_user(name=alt_tenant_user2,
+ password=password2,
+ tenantId=self.data.tenant['id'],
+ email='user2@123')['user']
user_ids.append(user2['id'])
self.data.users.append(user2)
# List of users for the respective tenant ID
@@ -175,19 +181,20 @@
user_ids = list()
fetched_user_ids = list()
user_ids.append(user['id'])
- role = self.roles_client.assign_user_role(tenant['id'], user['id'],
- role['id'])['role']
+ 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(alt_user2, alt_password2,
- self.data.tenant['id'],
- 'user2@123')['user']
+ second_user = self.users_client.create_user(
+ name=alt_user2,
+ password=alt_password2,
+ tenantId=self.data.tenant['id'],
+ email='user2@123')['user']
user_ids.append(second_user['id'])
self.data.users.append(second_user)
- role = self.roles_client.assign_user_role(tenant['id'],
- second_user['id'],
- role['id'])['role']
+ 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
body = (self.tenants_client.list_tenant_users(self.data.tenant['id'])
['users'])
diff --git a/tempest/api/identity/admin/v2/test_users_negative.py b/tempest/api/identity/admin/v2/test_users_negative.py
index 5fda4c14..78b89fa 100644
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ b/tempest/api/identity/admin/v2/test_users_negative.py
@@ -35,9 +35,9 @@
self.data.setup_test_tenant()
self.assertRaises(lib_exc.Forbidden,
self.non_admin_users_client.create_user,
- self.alt_user, self.alt_password,
- self.data.tenant['id'],
- self.alt_email)
+ name=self.alt_user, password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('d80d0c2f-4514-4d1e-806d-0930dfc5a187')
@@ -45,8 +45,9 @@
# User with an empty name should not be created
self.data.setup_test_tenant()
self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
- '', self.alt_password, self.data.tenant['id'],
- self.alt_email)
+ name='', password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('7704b4f3-3b75-4b82-87cc-931d41c8f780')
@@ -54,8 +55,9 @@
# Length of user name filed should be restricted to 255 characters
self.data.setup_test_tenant()
self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
- 'a' * 256, self.alt_password,
- self.data.tenant['id'], self.alt_email)
+ name='a' * 256, password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('57ae8558-120c-4723-9308-3751474e7ecf')
@@ -63,16 +65,20 @@
# Duplicate user should not be created
self.data.setup_test_user()
self.assertRaises(lib_exc.Conflict, self.users_client.create_user,
- self.data.user['name'], self.data.user_password,
- self.data.tenant['id'], self.data.user['email'])
+ name=self.data.user['name'],
+ password=self.data.user_password,
+ tenantId=self.data.tenant['id'],
+ email=self.data.user['email'])
@test.attr(type=['negative'])
@test.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
self.assertRaises(lib_exc.NotFound, self.users_client.create_user,
- self.alt_user, self.alt_password, '49ffgg99999',
- self.alt_email)
+ name=self.alt_user,
+ password=self.alt_password,
+ tenantId='49ffgg99999',
+ email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('55bbb103-d1ae-437b-989b-bcdf8175c1f4')
@@ -88,8 +94,9 @@
self.addCleanup(self.client.auth_provider.clear_auth)
self.assertRaises(lib_exc.Unauthorized, self.users_client.create_user,
- self.alt_user, self.alt_password,
- self.data.tenant['id'], self.alt_email)
+ name=self.alt_user, password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('23a2f3da-4a1a-41da-abdd-632328a861ad')
@@ -98,9 +105,9 @@
self.data.setup_test_tenant()
name = data_utils.rand_name('test_user')
self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
- name, self.alt_password,
- self.data.tenant['id'],
- self.alt_email, enabled=3)
+ name=name, password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email, enabled=3)
@test.attr(type=['negative'])
@test.idempotent_id('3d07e294-27a0-4144-b780-a2a1bf6fee19')
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index 27ff15d..24a7a4e 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -16,6 +16,7 @@
from tempest.api.identity import base
from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import test_utils
from tempest import test
CONF = config.CONF
@@ -23,44 +24,78 @@
class DomainsTestJSON(base.BaseIdentityV3AdminTest):
+ @classmethod
+ def resource_setup(cls):
+ super(DomainsTestJSON, cls).resource_setup()
+ # Create some test domains to be used during tests
+ # One of those domains will be disabled
+ cls.setup_domains = list()
+ for i in range(3):
+ domain = cls.domains_client.create_domain(
+ data_utils.rand_name('domain'),
+ description=data_utils.rand_name('domain-desc'),
+ enabled=i < 2)['domain']
+ cls.setup_domains.append(domain)
+
+ @classmethod
+ def resource_cleanup(cls):
+ for domain in cls.setup_domains:
+ cls._delete_domain(domain['id'])
+ super(DomainsTestJSON, cls).resource_cleanup()
+
+ @classmethod
def _delete_domain(self, domain_id):
# It is necessary to disable the domain before deleting,
# or else it would result in unauthorized error
self.domains_client.update_domain(domain_id, enabled=False)
self.domains_client.delete_domain(domain_id)
- # Asserting that the domain is not found in the list
- # after deletion
- body = self.domains_client.list_domains()['domains']
- domains_list = [d['id'] for d in body]
- self.assertNotIn(domain_id, domains_list)
@test.idempotent_id('8cf516ef-2114-48f1-907b-d32726c734d4')
def test_list_domains(self):
# Test to list domains
- domain_ids = list()
fetched_ids = list()
- for _ in range(3):
- domain = self.domains_client.create_domain(
- data_utils.rand_name('domain'),
- description=data_utils.rand_name('domain-desc'))['domain']
- # Delete the domain at the end of this method
- self.addCleanup(self._delete_domain, domain['id'])
- domain_ids.append(domain['id'])
# List and Verify Domains
body = self.domains_client.list_domains()['domains']
for d in body:
fetched_ids.append(d['id'])
- missing_doms = [d for d in domain_ids if d not in fetched_ids]
+ missing_doms = [d for d in self.setup_domains
+ if d['id'] not in fetched_ids]
self.assertEqual(0, len(missing_doms))
+ @test.idempotent_id('c6aee07b-4981-440c-bb0b-eb598f58ffe9')
+ def test_list_domains_filter_by_name(self):
+ # List domains filtering by name
+ params = {'name': self.setup_domains[0]['name']}
+ fetched_domains = self.domains_client.list_domains(
+ params=params)['domains']
+ # Verify the filtered list is correct, domain names are unique
+ # so exactly one domain should be found with the provided name
+ self.assertEqual(1, len(fetched_domains))
+ self.assertEqual(self.setup_domains[0]['name'],
+ fetched_domains[0]['name'])
+
+ @test.idempotent_id('3fd19840-65c1-43f8-b48c-51bdd066dff9')
+ def test_list_domains_filter_by_enabled(self):
+ # List domains filtering by enabled domains
+ params = {'enabled': True}
+ fetched_domains = self.domains_client.list_domains(
+ params=params)['domains']
+ # Verify the filtered list is correct
+ self.assertIn(self.setup_domains[0], fetched_domains)
+ self.assertIn(self.setup_domains[1], fetched_domains)
+ for domain in fetched_domains:
+ self.assertEqual(True, domain['enabled'])
+
@test.attr(type='smoke')
@test.idempotent_id('f2f5b44a-82e8-4dad-8084-0661ea3b18cf')
def test_create_update_delete_domain(self):
+ # Create domain
d_name = data_utils.rand_name('domain')
d_desc = data_utils.rand_name('domain-desc')
domain = self.domains_client.create_domain(
d_name, description=d_desc)['domain']
- self.addCleanup(self._delete_domain, domain['id'])
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self._delete_domain, domain['id'])
self.assertIn('id', domain)
self.assertIn('description', domain)
self.assertIn('name', domain)
@@ -70,11 +105,12 @@
self.assertEqual(d_name, domain['name'])
self.assertEqual(d_desc, domain['description'])
self.assertEqual(True, domain['enabled'])
+ # Update domain
new_desc = data_utils.rand_name('new-desc')
new_name = data_utils.rand_name('new-name')
-
updated_domain = self.domains_client.update_domain(
- domain['id'], name=new_name, description=new_desc)['domain']
+ domain['id'], name=new_name, description=new_desc,
+ enabled=False)['domain']
self.assertIn('id', updated_domain)
self.assertIn('description', updated_domain)
self.assertIn('name', updated_domain)
@@ -83,13 +119,18 @@
self.assertIsNotNone(updated_domain['id'])
self.assertEqual(new_name, updated_domain['name'])
self.assertEqual(new_desc, updated_domain['description'])
- self.assertEqual(True, updated_domain['enabled'])
-
+ self.assertEqual(False, updated_domain['enabled'])
+ # Show domain
fetched_domain = self.domains_client.show_domain(
domain['id'])['domain']
self.assertEqual(new_name, fetched_domain['name'])
self.assertEqual(new_desc, fetched_domain['description'])
- self.assertEqual(True, fetched_domain['enabled'])
+ self.assertEqual(False, fetched_domain['enabled'])
+ # Delete domain
+ self.domains_client.delete_domain(domain['id'])
+ body = self.domains_client.list_domains()['domains']
+ domains_list = [d['id'] for d in body]
+ self.assertNotIn(domain['id'], domains_list)
@test.idempotent_id('036df86e-bb5d-42c0-a7c2-66b9db3a6046')
def test_create_domain_with_disabled_status(self):
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 928437c..86f6b12 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -37,9 +37,15 @@
cls.p2 = cls.projects_client.create_project(p2_name)['project']
cls.data.projects.append(cls.p2)
cls.project_ids.append(cls.p2['id'])
+ # Create a new project (p3) using p2 as parent project
+ p3_name = data_utils.rand_name('project')
+ cls.p3 = cls.projects_client.create_project(
+ p3_name, parent_id=cls.p2['id'])['project']
+ cls.data.projects.append(cls.p3)
+ cls.project_ids.append(cls.p3['id'])
@test.idempotent_id('1d830662-22ad-427c-8c3e-4ec854b0af44')
- def test_projects_list(self):
+ def test_list_projects(self):
# List projects
list_projects = self.projects_client.list_projects()['projects']
@@ -63,6 +69,16 @@
# List projects with name
self._list_projects_with_params({'name': self.p1_name}, 'name')
+ @test.idempotent_id('6edc66f5-2941-4a17-9526-4073311c1fac')
+ def test_list_projects_with_parent(self):
+ # List projects with parent
+ params = {'parent_id': self.p3['parent_id']}
+ fetched_projects = self.projects_client.list_projects(
+ params)['projects']
+ self.assertNotEmpty(fetched_projects)
+ for project in fetched_projects:
+ self.assertEqual(self.p3['parent_id'], project['parent_id'])
+
def _list_projects_with_params(self, params, key):
body = self.projects_client.list_projects(params)['projects']
self.assertIn(self.p1[key], map(lambda x: x[key], body))
diff --git a/tempest/api/identity/admin/v3/test_regions.py b/tempest/api/identity/admin/v3/test_regions.py
index ece36b9..95894a6 100644
--- a/tempest/api/identity/admin/v3/test_regions.py
+++ b/tempest/api/identity/admin/v3/test_regions.py
@@ -15,7 +15,7 @@
from tempest.api.identity import base
from tempest.common.utils import data_utils
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
from tempest import test
@@ -42,18 +42,19 @@
cls.client.delete_region(r['id'])
super(RegionsTestJSON, cls).resource_cleanup()
- def _delete_region(self, region_id):
- self.client.delete_region(region_id)
- self.assertRaises(lib_exc.NotFound,
- self.client.show_region, region_id)
-
@test.idempotent_id('56186092-82e4-43f2-b954-91013218ba42')
def test_create_update_get_delete_region(self):
+ # Create region
r_description = data_utils.rand_name('description')
region = self.client.create_region(
description=r_description,
parent_region_id=self.setup_regions[0]['id'])['region']
- self.addCleanup(self._delete_region, region['id'])
+ # This test will delete the region as part of the validation
+ # procedure, so it needs a different cleanup method that
+ # would be useful in case the tests fails at any point before
+ # reaching the deletion part.
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.client.delete_region, region['id'])
self.assertEqual(r_description, region['description'])
self.assertEqual(self.setup_regions[0]['id'],
region['parent_region_id'])
@@ -71,6 +72,11 @@
self.assertEqual(r_alt_description, region['description'])
self.assertEqual(self.setup_regions[1]['id'],
region['parent_region_id'])
+ # Delete the region
+ self.client.delete_region(region['id'])
+ body = self.client.list_regions()['regions']
+ regions_list = [r['id'] for r in body]
+ self.assertNotIn(region['id'], regions_list)
@test.attr(type='smoke')
@test.idempotent_id('2c12c5b5-efcf-4aa5-90c5-bff1ab0cdbe2')
@@ -80,7 +86,7 @@
r_description = data_utils.rand_name('description')
region = self.client.create_region(
region_id=r_region_id, description=r_description)['region']
- self.addCleanup(self._delete_region, region['id'])
+ self.addCleanup(self.client.delete_region, region['id'])
# Asserting Create Region with specific id response body
self.assertEqual(r_region_id, region['id'])
self.assertEqual(r_description, region['description'])
@@ -95,3 +101,20 @@
self.assertEqual(0, len(missing_regions),
"Failed to find region %s in fetched list" %
', '.join(str(e) for e in missing_regions))
+
+ @test.idempotent_id('2d1057cb-bbde-413a-acdf-e2d265284542')
+ def test_list_regions_filter_by_parent_region_id(self):
+ # Add a sub-region to one of the existing test regions
+ r_description = data_utils.rand_name('description')
+ region = self.client.create_region(
+ description=r_description,
+ parent_region_id=self.setup_regions[0]['id'])['region']
+ self.addCleanup(self.client.delete_region, region['id'])
+ # Get the list of regions filtering with the parent_region_id
+ params = {'parent_region_id': self.setup_regions[0]['id']}
+ fetched_regions = self.client.list_regions(params=params)['regions']
+ # Asserting list regions response
+ self.assertIn(region, fetched_regions)
+ for r in fetched_regions:
+ self.assertEqual(self.setup_regions[0]['id'],
+ r['parent_region_id'])
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index 6f12939..89cfd5b 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import six
+
from tempest.api.identity import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
@@ -97,8 +99,8 @@
orig_expires_at = token_auth['token']['expires_at']
orig_user = token_auth['token']['user']
- self.assertIsInstance(token_auth['token']['expires_at'], unicode)
- self.assertIsInstance(token_auth['token']['issued_at'], unicode)
+ self.assertIsInstance(token_auth['token']['expires_at'], six.text_type)
+ self.assertIsInstance(token_auth['token']['issued_at'], six.text_type)
self.assertEqual(['password'], token_auth['token']['methods'])
self.assertEqual(user['id'], token_auth['token']['user']['id'])
self.assertEqual(user['name'], token_auth['token']['user']['name'])
@@ -118,7 +120,7 @@
self.assertEqual(orig_expires_at, token_auth['token']['expires_at'],
'Expiration time should match original token')
- self.assertIsInstance(token_auth['token']['issued_at'], unicode)
+ self.assertIsInstance(token_auth['token']['issued_at'], six.text_type)
self.assertEqual(set(['password', 'token']),
set(token_auth['token']['methods']))
self.assertEqual(orig_user, token_auth['token']['user'],
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 09ae468..9c8f1f6 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -98,7 +98,8 @@
password=self.trustor_password,
user_domain_id='default',
tenant_name=self.trustor_project_name,
- project_domain_id='default')
+ project_domain_id='default',
+ domain_id='default')
os = clients.Manager(credentials=creds)
self.trustor_client = os.trusts_client
@@ -266,7 +267,18 @@
@test.attr(type='smoke')
@test.idempotent_id('4773ebd5-ecbf-4255-b8d8-b63e6f72b65d')
def test_get_trusts_all(self):
+
+ # Simple function that can be used for cleanup
+ def set_scope(auth_provider, scope):
+ auth_provider.scope = scope
+
self.create_trust()
+ # Listing trusts can be done by trustor, by trustee, or without
+ # any filter if scoped to a project, so we must ensure token scope is
+ # project for this test.
+ original_scope = self.os_adm.auth_provider.scope
+ set_scope(self.os_adm.auth_provider, 'project')
+ self.addCleanup(set_scope, self.os_adm.auth_provider, original_scope)
trusts_get = self.trusts_client.list_trusts()['trusts']
trusts = [t for t in trusts_get
if t['id'] == self.trust_id]
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 2d4ff17..7a1e3a5 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -29,7 +29,7 @@
@classmethod
def disable_user(cls, user_name):
user = cls.get_user_by_name(user_name)
- cls.users_client.enable_disable_user(user['id'], enabled=False)
+ cls.users_client.update_user_enabled(user['id'], enabled=False)
@classmethod
def disable_tenant(cls, tenant_name):
@@ -81,14 +81,6 @@
cls.non_admin_tenants_client = cls.os.tenants_public_client
cls.non_admin_users_client = cls.os.users_public_client
- @classmethod
- def resource_setup(cls):
- super(BaseIdentityV2Test, cls).resource_setup()
-
- @classmethod
- def resource_cleanup(cls):
- super(BaseIdentityV2Test, cls).resource_cleanup()
-
class BaseIdentityV2AdminTest(BaseIdentityV2Test):
@@ -137,10 +129,6 @@
cls.non_admin_token = cls.os.token_v3_client
cls.non_admin_projects_client = cls.os.projects_client
- @classmethod
- def resource_cleanup(cls):
- super(BaseIdentityV3Test, cls).resource_cleanup()
-
class BaseIdentityV3AdminTest(BaseIdentityV3Test):
@@ -162,6 +150,12 @@
cls.creds_client = cls.os_adm.credentials_client
cls.groups_client = cls.os_adm.groups_client
cls.projects_client = cls.os_adm.projects_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.
+ # As the client manager is already created by the base class,
+ # we set the scope for the inner auth provider.
+ cls.os_adm.auth_provider.scope = 'domain'
@classmethod
def resource_setup(cls):
@@ -209,11 +203,10 @@
self.domains = []
def _create_test_user(self, **kwargs):
- username = data_utils.rand_name('test_user')
self.user_password = data_utils.rand_password()
self.user = self.users_client.create_user(
- username, password=self.user_password,
- email=username + '@testmail.tm', **kwargs)['user']
+ password=self.user_password,
+ **kwargs)['user']
self.users.append(self.user)
def setup_test_role(self):
@@ -222,27 +215,24 @@
name=data_utils.rand_name('test_role'))['role']
self.roles.append(self.role)
- @staticmethod
- def _try_wrapper(func, item, **kwargs):
- try:
- test_utils.call_and_ignore_notfound_exc(func, item['id'], **kwargs)
- except Exception:
- LOG.exception("Unexpected exception occurred in %s deletion. "
- "But ignored here." % item['id'])
-
def teardown_all(self):
for user in self.users:
- self._try_wrapper(self.users_client.delete_user, user)
+ test_utils.call_and_ignore_notfound_exc(
+ self.users_client.delete_user, user)
for tenant in self.tenants:
- self._try_wrapper(self.projects_client.delete_tenant, tenant)
- for project in self.projects:
- self._try_wrapper(self.projects_client.delete_project, project)
+ test_utils.call_and_ignore_notfound_exc(
+ self.projects_client.delete_tenant, tenant)
+ for project in reversed(self.projects):
+ test_utils.call_and_ignore_notfound_exc(
+ self.projects_client.delete_project, project)
for role in self.roles:
- self._try_wrapper(self.roles_client.delete_role, role)
+ test_utils.call_and_ignore_notfound_exc(
+ self.roles_client.delete_role, role)
for domain in self.domains:
- self._try_wrapper(self.domains_client.update_domain, domain,
- enabled=False)
- self._try_wrapper(self.domains_client.delete_domain, domain)
+ test_utils.call_and_ignore_notfound_exc(
+ self.domains_client.update_domain, domain, enabled=False)
+ test_utils.call_and_ignore_notfound_exc(
+ self.domains_client.delete_domain, domain)
class DataGeneratorV2(BaseDataGenerator):
@@ -250,7 +240,10 @@
def setup_test_user(self):
"""Set up a test user."""
self.setup_test_tenant()
- self._create_test_user(tenant_id=self.tenant['id'])
+ username = data_utils.rand_name('test_user')
+ email = username + '@testmail.tm'
+ self._create_test_user(name=username, email=email,
+ tenantId=self.tenant['id'])
def setup_test_tenant(self):
"""Set up a test tenant."""
@@ -265,7 +258,10 @@
def setup_test_user(self):
"""Set up a test user."""
self.setup_test_project()
- self._create_test_user(project_id=self.project['id'])
+ username = data_utils.rand_name('test_user')
+ email = username + '@testmail.tm'
+ self._create_test_user(user_name=username, email=email,
+ project_id=self.project['id'])
def setup_test_project(self):
"""Set up a test project."""
diff --git a/tempest/api/identity/v2/test_ec2_credentials.py b/tempest/api/identity/v2/test_ec2_credentials.py
index 8600980..3c379f0 100644
--- a/tempest/api/identity/v2/test_ec2_credentials.py
+++ b/tempest/api/identity/v2/test_ec2_credentials.py
@@ -33,19 +33,19 @@
cls.creds = cls.os.credentials
@test.idempotent_id('b580fab9-7ae9-46e8-8138-417260cb6f9f')
- def test_create_ec2_credentials(self):
- """Create user ec2 credentials."""
- resp = self.non_admin_users_client.create_user_ec2_credentials(
- self.creds.credentials.user_id,
- tenant_id=self.creds.credentials.tenant_id)["credential"]
+ def test_create_ec2_credential(self):
+ """Create user ec2 credential."""
+ resp = self.non_admin_users_client.create_user_ec2_credential(
+ self.creds.user_id,
+ tenant_id=self.creds.tenant_id)["credential"]
access = resp['access']
self.addCleanup(
- self.non_admin_users_client.delete_user_ec2_credentials,
- self.creds.credentials.user_id, access)
+ self.non_admin_users_client.delete_user_ec2_credential,
+ self.creds.user_id, access)
self.assertNotEmpty(resp['access'])
self.assertNotEmpty(resp['secret'])
- self.assertEqual(self.creds.credentials.user_id, resp['user_id'])
- self.assertEqual(self.creds.credentials.tenant_id, resp['tenant_id'])
+ self.assertEqual(self.creds.user_id, resp['user_id'])
+ self.assertEqual(self.creds.tenant_id, resp['tenant_id'])
@test.idempotent_id('9e2ea42f-0a4f-468c-a768-51859ce492e0')
def test_list_ec2_credentials(self):
@@ -53,25 +53,25 @@
created_creds = []
fetched_creds = []
# create first ec2 credentials
- creds1 = self.non_admin_users_client.create_user_ec2_credentials(
- self.creds.credentials.user_id,
- tenant_id=self.creds.credentials.tenant_id)["credential"]
+ creds1 = self.non_admin_users_client.create_user_ec2_credential(
+ self.creds.user_id,
+ tenant_id=self.creds.tenant_id)["credential"]
created_creds.append(creds1['access'])
# create second ec2 credentials
- creds2 = self.non_admin_users_client.create_user_ec2_credentials(
- self.creds.credentials.user_id,
- tenant_id=self.creds.credentials.tenant_id)["credential"]
+ creds2 = self.non_admin_users_client.create_user_ec2_credential(
+ self.creds.user_id,
+ tenant_id=self.creds.tenant_id)["credential"]
created_creds.append(creds2['access'])
# add credentials to be cleaned up
self.addCleanup(
- self.non_admin_users_client.delete_user_ec2_credentials,
- self.creds.credentials.user_id, creds1['access'])
+ self.non_admin_users_client.delete_user_ec2_credential,
+ self.creds.user_id, creds1['access'])
self.addCleanup(
- self.non_admin_users_client.delete_user_ec2_credentials,
- self.creds.credentials.user_id, creds2['access'])
+ self.non_admin_users_client.delete_user_ec2_credential,
+ self.creds.user_id, creds2['access'])
# get the list of user ec2 credentials
resp = self.non_admin_users_client.list_user_ec2_credentials(
- self.creds.credentials.user_id)["credentials"]
+ self.creds.user_id)["credentials"]
fetched_creds = [cred['access'] for cred in resp]
# created credentials should be in a fetched list
missing = [cred for cred in created_creds
@@ -81,32 +81,32 @@
', '.join(cred for cred in missing))
@test.idempotent_id('cb284075-b613-440d-83ca-fe0b33b3c2b8')
- def test_show_ec2_credentials(self):
- """Get the definite user ec2 credentials."""
- resp = self.non_admin_users_client.create_user_ec2_credentials(
- self.creds.credentials.user_id,
- tenant_id=self.creds.credentials.tenant_id)["credential"]
+ def test_show_ec2_credential(self):
+ """Get the definite user ec2 credential."""
+ resp = self.non_admin_users_client.create_user_ec2_credential(
+ self.creds.user_id,
+ tenant_id=self.creds.tenant_id)["credential"]
self.addCleanup(
- self.non_admin_users_client.delete_user_ec2_credentials,
- self.creds.credentials.user_id, resp['access'])
+ self.non_admin_users_client.delete_user_ec2_credential,
+ self.creds.user_id, resp['access'])
- ec2_creds = self.non_admin_users_client.show_user_ec2_credentials(
- self.creds.credentials.user_id, resp['access']
+ ec2_creds = self.non_admin_users_client.show_user_ec2_credential(
+ self.creds.user_id, resp['access']
)["credential"]
for key in ['access', 'secret', 'user_id', 'tenant_id']:
self.assertEqual(ec2_creds[key], resp[key])
@test.idempotent_id('6aba0d4c-b76b-4e46-aa42-add79bc1551d')
- def test_delete_ec2_credentials(self):
- """Delete user ec2 credentials."""
- resp = self.non_admin_users_client.create_user_ec2_credentials(
- self.creds.credentials.user_id,
- tenant_id=self.creds.credentials.tenant_id)["credential"]
+ def test_delete_ec2_credential(self):
+ """Delete user ec2 credential."""
+ resp = self.non_admin_users_client.create_user_ec2_credential(
+ self.creds.user_id,
+ tenant_id=self.creds.tenant_id)["credential"]
access = resp['access']
- self.non_admin_users_client.delete_user_ec2_credentials(
- self.creds.credentials.user_id, access)
+ self.non_admin_users_client.delete_user_ec2_credential(
+ self.creds.user_id, access)
self.assertRaises(
lib_exc.NotFound,
- self.non_admin_users_client.show_user_ec2_credentials,
- self.creds.credentials.user_id,
+ self.non_admin_users_client.show_user_ec2_credential,
+ self.creds.user_id,
access)
diff --git a/tempest/api/identity/v2/test_tenants.py b/tempest/api/identity/v2/test_tenants.py
index b742e69..cc6de47 100644
--- a/tempest/api/identity/v2/test_tenants.py
+++ b/tempest/api/identity/v2/test_tenants.py
@@ -24,7 +24,7 @@
@test.idempotent_id('ecae2459-243d-4ba1-ad02-65f15dc82b78')
def test_list_tenants_returns_only_authorized_tenants(self):
- alt_tenant_name = self.alt_manager.credentials.credentials.tenant_name
+ alt_tenant_name = self.alt_manager.credentials.tenant_name
resp = self.non_admin_tenants_client.list_tenants()
# check that user can see only that tenants that he presents in so user
diff --git a/tempest/api/identity/v2/test_tokens.py b/tempest/api/identity/v2/test_tokens.py
index 3b508f4..bdca1e0 100644
--- a/tempest/api/identity/v2/test_tokens.py
+++ b/tempest/api/identity/v2/test_tokens.py
@@ -43,8 +43,8 @@
self.assertGreater(expires_at, now)
self.assertEqual(body['token']['tenant']['id'],
- creds.credentials.tenant_id)
+ creds.tenant_id)
self.assertEqual(body['token']['tenant']['name'],
tenant_name)
- self.assertEqual(body['user']['id'], creds.credentials.user_id)
+ self.assertEqual(body['user']['id'], creds.user_id)
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
index 79f2576..4833f9e 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -46,9 +46,9 @@
client.auth_provider.clear_auth()
client.auth_provider.set_auth()
- old_pass = self.creds.credentials.password
+ old_pass = self.creds.password
new_pass = data_utils.rand_password()
- user_id = self.creds.credentials.user_id
+ user_id = self.creds.user_id
# to change password back. important for allow_tenant_isolation = false
self.addCleanup(_restore_password, self.non_admin_users_client,
user_id, old_pass=old_pass, new_pass=new_pass)
diff --git a/tempest/api/identity/v3/test_projects.py b/tempest/api/identity/v3/test_projects.py
index 1574ab7..26cb90b 100644
--- a/tempest/api/identity/v3/test_projects.py
+++ b/tempest/api/identity/v3/test_projects.py
@@ -25,7 +25,7 @@
@test.idempotent_id('86128d46-e170-4644-866a-cc487f699e1d')
def test_list_projects_returns_only_authorized_projects(self):
alt_project_name =\
- self.alt_manager.credentials.credentials.project_name
+ self.alt_manager.credentials.project_name
resp = self.non_admin_users_client.list_user_projects(
self.os.credentials.user_id)
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index 76b46c0..c92e750 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -46,9 +46,9 @@
client.auth_provider.clear_auth()
client.auth_provider.set_auth()
- old_pass = self.creds.credentials.password
+ old_pass = self.creds.password
new_pass = data_utils.rand_password()
- user_id = self.creds.credentials.user_id
+ user_id = self.creds.user_id
# to change password back. important for allow_tenant_isolation = false
self.addCleanup(_restore_password, self.non_admin_users_client,
user_id, old_pass=old_pass, new_pass=new_pass)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 6d0fa8e..3fefc81 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -126,6 +126,7 @@
super(BaseV2ImageTest, cls).setup_clients()
cls.client = cls.os.image_client_v2
cls.namespaces_client = cls.os.namespaces_client
+ cls.resource_types_client = cls.os.resource_types_client
cls.schemas_client = cls.os.schemas_client
@@ -136,7 +137,7 @@
@classmethod
def setup_clients(cls):
super(BaseV2MemberImageTest, cls).setup_clients()
- cls.os_image_member_client = cls.os.image_member_client_v2
+ cls.image_member_client = cls.os.image_member_client_v2
cls.alt_image_member_client = cls.os_alt.image_member_client_v2
cls.alt_img_client = cls.os_alt.image_client_v2
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index 0bad96a..94edb6c 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -22,7 +22,7 @@
@test.idempotent_id('1d6ef640-3a20-4c84-8710-d95828fdb6ad')
def test_add_image_member(self):
image = self._create_image()
- self.image_member_client.add_member(self.alt_tenant_id, image)
+ self.image_member_client.create_image_member(image, self.alt_tenant_id)
body = self.image_member_client.list_image_members(image)
members = body['members']
members = map(lambda x: x['member_id'], members)
@@ -33,9 +33,10 @@
@test.idempotent_id('6a5328a5-80e8-4b82-bd32-6c061f128da9')
def test_get_shared_images(self):
image = self._create_image()
- self.image_member_client.add_member(self.alt_tenant_id, image)
+ self.image_member_client.create_image_member(image, self.alt_tenant_id)
share_image = self._create_image()
- self.image_member_client.add_member(self.alt_tenant_id, share_image)
+ self.image_member_client.create_image_member(share_image,
+ self.alt_tenant_id)
body = self.image_member_client.list_shared_images(
self.alt_tenant_id)
images = body['shared_images']
@@ -46,8 +47,10 @@
@test.idempotent_id('a76a3191-8948-4b44-a9d6-4053e5f2b138')
def test_remove_member(self):
image_id = self._create_image()
- self.image_member_client.add_member(self.alt_tenant_id, image_id)
- self.image_member_client.delete_member(self.alt_tenant_id, image_id)
+ self.image_member_client.create_image_member(image_id,
+ self.alt_tenant_id)
+ self.image_member_client.delete_image_member(image_id,
+ self.alt_tenant_id)
body = self.image_member_client.list_image_members(image_id)
members = body['members']
self.assertEqual(0, len(members), str(members))
diff --git a/tempest/api/image/v1/test_image_members_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index d46a836..2538781 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -26,8 +26,8 @@
# Add member with non existing image.
non_exist_image = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
- self.image_member_client.add_member,
- self.alt_tenant_id, non_exist_image)
+ self.image_member_client.create_image_member,
+ non_exist_image, self.alt_tenant_id)
@test.attr(type=['negative'])
@test.idempotent_id('e1559f05-b667-4f1b-a7af-518b52dc0c0f')
@@ -35,8 +35,8 @@
# Delete member with non existing image.
non_exist_image = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
- self.image_member_client.delete_member,
- self.alt_tenant_id, non_exist_image)
+ self.image_member_client.delete_image_member,
+ non_exist_image, self.alt_tenant_id)
@test.attr(type=['negative'])
@test.idempotent_id('f5720333-dd69-4194-bb76-d2f048addd56')
@@ -45,8 +45,8 @@
image_id = self._create_image()
non_exist_tenant = data_utils.rand_uuid_hex()
self.assertRaises(lib_exc.NotFound,
- self.image_member_client.delete_member,
- non_exist_tenant, image_id)
+ self.image_member_client.delete_image_member,
+ image_id, non_exist_tenant)
@test.attr(type=['negative'])
@test.idempotent_id('f25f89e4-0b6c-453b-a853-1f80b9d7ef26')
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 6d5559d..59ac646 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -16,6 +16,7 @@
from six import moves
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
@@ -306,7 +307,8 @@
@test.idempotent_id('01752c1c-0275-4de3-9e5b-876e44541928')
def test_list_image_metadata(self):
# All metadata key/value pairs for an image should be returned
- resp_metadata = self.client.check_image(self.image_id)
+ resp = self.client.check_image(self.image_id)
+ resp_metadata = common_image.get_image_meta_from_headers(resp)
expected = {'key1': 'value1'}
self.assertEqual(expected, resp_metadata['properties'])
@@ -314,12 +316,14 @@
def test_update_image_metadata(self):
# The metadata for the image should match the updated values
req_metadata = {'key1': 'alt1', 'key2': 'value2'}
- metadata = self.client.check_image(self.image_id)
+ resp = self.client.check_image(self.image_id)
+ metadata = common_image.get_image_meta_from_headers(resp)
self.assertEqual(metadata['properties'], {'key1': 'value1'})
metadata['properties'].update(req_metadata)
metadata = self.client.update_image(
self.image_id, properties=metadata['properties'])['image']
- resp_metadata = self.client.check_image(self.image_id)
+ resp = self.client.check_image(self.image_id)
+ resp_metadata = common_image.get_image_meta_from_headers(resp)
expected = {'key1': 'alt1', 'key2': 'value2'}
self.assertEqual(expected, resp_metadata['properties'])
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index d8254f5..fe8dd65 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -19,7 +19,7 @@
@test.idempotent_id('5934c6ea-27dc-4d6e-9421-eeb5e045494a')
def test_image_share_accept(self):
image_id = self._create_image()
- member = self.os_image_member_client.create_image_member(
+ 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)
@@ -29,7 +29,7 @@
self.alt_tenant_id,
status='accepted')
self.assertIn(image_id, self._list_image_ids_as_alt())
- body = self.os_image_member_client.list_image_members(image_id)
+ body = self.image_member_client.list_image_members(image_id)
members = body['members']
member = members[0]
self.assertEqual(len(members), 1, str(members))
@@ -40,7 +40,7 @@
@test.idempotent_id('d9e83e5f-3524-4b38-a900-22abcb26e90e')
def test_image_share_reject(self):
image_id = self._create_image()
- member = self.os_image_member_client.create_image_member(
+ 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)
@@ -54,14 +54,14 @@
@test.idempotent_id('a6ee18b9-4378-465e-9ad9-9a6de58a3287')
def test_get_image_member(self):
image_id = self._create_image()
- self.os_image_member_client.create_image_member(
+ self.image_member_client.create_image_member(
image_id, member=self.alt_tenant_id)
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())
- member = self.os_image_member_client.show_image_member(
+ member = self.image_member_client.show_image_member(
image_id, self.alt_tenant_id)
self.assertEqual(self.alt_tenant_id, member['member_id'])
self.assertEqual(image_id, member['image_id'])
@@ -70,15 +70,15 @@
@test.idempotent_id('72989bc7-2268-48ed-af22-8821e835c914')
def test_remove_image_member(self):
image_id = self._create_image()
- self.os_image_member_client.create_image_member(
+ self.image_member_client.create_image_member(
image_id, member=self.alt_tenant_id)
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.os_image_member_client.delete_image_member(image_id,
- self.alt_tenant_id)
+ self.image_member_client.delete_image_member(image_id,
+ self.alt_tenant_id)
self.assertNotIn(image_id, self._list_image_ids_as_alt())
@test.idempotent_id('634dcc3f-f6e2-4409-b8fd-354a0bb25d83')
@@ -94,7 +94,7 @@
@test.idempotent_id('cb961424-3f68-4d21-8e36-30ad66fb6bfb')
def test_get_private_image(self):
image_id = self._create_image()
- member = self.os_image_member_client.create_image_member(
+ 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)
@@ -104,6 +104,6 @@
self.alt_tenant_id,
status='accepted')
self.assertIn(image_id, self._list_image_ids_as_alt())
- self.os_image_member_client.delete_image_member(image_id,
- self.alt_tenant_id)
+ 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 5a08d77..fa29a92 100644
--- a/tempest/api/image/v2/test_images_member_negative.py
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -21,7 +21,7 @@
@test.idempotent_id('b79efb37-820d-4cf0-b54c-308b00cf842c')
def test_image_share_invalid_status(self):
image_id = self._create_image()
- member = self.os_image_member_client.create_image_member(
+ member = self.image_member_client.create_image_member(
image_id, member=self.alt_tenant_id)
self.assertEqual(member['status'], 'pending')
self.assertRaises(lib_exc.BadRequest,
@@ -33,11 +33,11 @@
@test.idempotent_id('27002f74-109e-4a37-acd0-f91cd4597967')
def test_image_share_owner_cannot_accept(self):
image_id = self._create_image()
- member = self.os_image_member_client.create_image_member(
+ member = self.image_member_client.create_image_member(
image_id, member=self.alt_tenant_id)
self.assertEqual(member['status'], 'pending')
self.assertNotIn(image_id, self._list_image_ids_as_alt())
self.assertRaises(lib_exc.Forbidden,
- self.os_image_member_client.update_image_member,
+ self.image_member_client.update_image_member,
image_id, self.alt_tenant_id, status='accepted')
self.assertNotIn(image_id, self._list_image_ids_as_alt())
diff --git a/tempest/api/image/v2/test_images_metadefs_namespaces.py b/tempest/api/image/v2/test_images_metadefs_namespaces.py
index f6d1bdc..6fced00 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespaces.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespaces.py
@@ -26,7 +26,7 @@
@test.idempotent_id('319b765e-7f3d-4b3d-8b37-3ca3876ee768')
def test_basic_metadata_definition_namespaces(self):
# get the available resource types and use one resource_type
- body = self.client.list_resource_types()
+ body = self.resource_types_client.list_resource_types()
resource_name = body['resource_types'][0]['name']
name = [{'name': resource_name}]
namespace_name = data_utils.rand_name('namespace')
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 89fa576..9e7c795 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -80,6 +80,7 @@
cls.security_groups_client = cls.os.security_groups_client
cls.security_group_rules_client = (
cls.os.security_group_rules_client)
+ cls.network_versions_client = cls.os.network_versions_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 1d96439..bf80ff5 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -565,9 +565,9 @@
subnets = [sub['id'] for sub in body['subnets']
if sub['network_id'] == network['id']]
test_subnet_ids = [sub['id'] for sub in (subnet1, subnet2)]
- self.assertItemsEqual(subnets,
- test_subnet_ids,
- 'Subnet are not in the same network')
+ six.assertCountEqual(self, subnets,
+ test_subnet_ids,
+ 'Subnet are not in the same network')
class NetworksIpV6TestAttrs(NetworksIpV6Test):
diff --git a/tempest/api/network/test_versions.py b/tempest/api/network/test_versions.py
new file mode 100644
index 0000000..9cf93f6
--- /dev/null
+++ b/tempest/api/network/test_versions.py
@@ -0,0 +1,40 @@
+# Copyright 2016 VMware, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.network import base
+from tempest import test
+
+
+class NetworksApiDiscovery(base.BaseNetworkTest):
+ @test.attr(type='smoke')
+ @test.idempotent_id('cac8a836-c2e0-4304-b556-cd299c7281d1')
+ def test_api_version_resources(self):
+ """Test that GET / returns expected resources.
+
+ The versions document returned by Neutron returns a few other
+ resources other than just available API versions: it also
+ states the status of each API version and provides links to
+ schema.
+ """
+
+ result = self.network_versions_client.list_versions()
+ expected_versions = ('v2.0')
+ expected_resources = ('id', 'links', 'status')
+ received_list = result.values()
+
+ for item in received_list:
+ for version in item:
+ for resource in expected_resources:
+ self.assertIn(resource, version)
+ self.assertIn(version['id'], expected_versions)
diff --git a/tempest/api/orchestration/stacks/test_stacks.py b/tempest/api/orchestration/stacks/test_stacks.py
index 28463ab..f13a2d9 100644
--- a/tempest/api/orchestration/stacks/test_stacks.py
+++ b/tempest/api/orchestration/stacks/test_stacks.py
@@ -18,10 +18,6 @@
class StacksTestJSON(base.BaseOrchestrationTest):
empty_template = "HeatTemplateFormatVersion: '2012-12-12'\n"
- @classmethod
- def resource_setup(cls):
- super(StacksTestJSON, cls).resource_setup()
-
@test.attr(type='smoke')
@test.idempotent_id('d35d628c-07f6-4674-85a1-74db9919e986')
def test_stack_list_responds(self):
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 00acc7d..5e1c20b 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -63,7 +63,7 @@
extra_specs = {spec_key_with_prefix: backend_name_key}
else:
extra_specs = {spec_key_without_prefix: backend_name_key}
- self.type = self.volume_types_client.create_volume_type(
+ self.type = self.admin_volume_types_client.create_volume_type(
name=type_name, extra_specs=extra_specs)['volume_type']
self.volume_type_id_list.append(self.type['id'])
@@ -95,7 +95,7 @@
# volume types deletion
volume_type_id_list = getattr(cls, 'volume_type_id_list', [])
for volume_type_id in volume_type_id_list:
- cls.volume_types_client.delete_volume_type(volume_type_id)
+ cls.admin_volume_types_client.delete_volume_type(volume_type_id)
super(VolumeMultiBackendV2Test, cls).resource_cleanup()
diff --git a/tempest/api/volume/admin/test_qos.py b/tempest/api/volume/admin/test_qos.py
index 722a39a..68e57f5 100644
--- a/tempest/api/volume/admin/test_qos.py
+++ b/tempest/api/volume/admin/test_qos.py
@@ -43,27 +43,27 @@
for key in ['name', 'consumer']:
self.assertEqual(qos[key], body[key])
- self.volume_qos_client.delete_qos(body['id'])
- self.volume_qos_client.wait_for_resource_deletion(body['id'])
+ self.admin_volume_qos_client.delete_qos(body['id'])
+ self.admin_volume_qos_client.wait_for_resource_deletion(body['id'])
# validate the deletion
- list_qos = self.volume_qos_client.list_qos()['qos_specs']
+ list_qos = self.admin_volume_qos_client.list_qos()['qos_specs']
self.assertNotIn(body, list_qos)
def _create_test_volume_type(self):
vol_type_name = utils.rand_name("volume-type")
- vol_type = self.volume_types_client.create_volume_type(
+ vol_type = self.admin_volume_types_client.create_volume_type(
name=vol_type_name)['volume_type']
- self.addCleanup(self.volume_types_client.delete_volume_type,
+ self.addCleanup(self.admin_volume_types_client.delete_volume_type,
vol_type['id'])
return vol_type
def _test_associate_qos(self, vol_type_id):
- self.volume_qos_client.associate_qos(
+ self.admin_volume_qos_client.associate_qos(
self.created_qos['id'], vol_type_id)
def _test_get_association_qos(self):
- body = self.volume_qos_client.show_association_qos(
+ body = self.admin_volume_qos_client.show_association_qos(
self.created_qos['id'])['qos_associations']
associations = []
@@ -99,7 +99,7 @@
@test.idempotent_id('7aa214cc-ac1a-4397-931f-3bb2e83bb0fd')
def test_get_qos(self):
"""Tests the detail of a given qos-specs"""
- body = self.volume_qos_client.show_qos(
+ body = self.admin_volume_qos_client.show_qos(
self.created_qos['id'])['qos_specs']
self.assertEqual(self.qos_name, body['name'])
self.assertEqual(self.qos_consumer, body['consumer'])
@@ -107,28 +107,29 @@
@test.idempotent_id('75e04226-bcf7-4595-a34b-fdf0736f38fc')
def test_list_qos(self):
"""Tests the list of all qos-specs"""
- body = self.volume_qos_client.list_qos()['qos_specs']
+ body = self.admin_volume_qos_client.list_qos()['qos_specs']
self.assertIn(self.created_qos, body)
@test.idempotent_id('ed00fd85-4494-45f2-8ceb-9e2048919aed')
def test_set_unset_qos_key(self):
"""Test the addition of a specs key to qos-specs"""
args = {'iops_bytes': '500'}
- body = self.volume_qos_client.set_qos_key(
+ body = self.admin_volume_qos_client.set_qos_key(
self.created_qos['id'],
iops_bytes='500')['qos_specs']
self.assertEqual(args, body)
- body = self.volume_qos_client.show_qos(
+ body = self.admin_volume_qos_client.show_qos(
self.created_qos['id'])['qos_specs']
self.assertEqual(args['iops_bytes'], body['specs']['iops_bytes'])
# test the deletion of a specs key from qos-specs
keys = ['iops_bytes']
- self.volume_qos_client.unset_qos_key(self.created_qos['id'], keys)
+ self.admin_volume_qos_client.unset_qos_key(self.created_qos['id'],
+ keys)
operation = 'qos-key-unset'
- self.volume_qos_client.wait_for_qos_operations(self.created_qos['id'],
- operation, keys)
- body = self.volume_qos_client.show_qos(
+ self.admin_volume_qos_client.wait_for_qos_operations(
+ self.created_qos['id'], operation, keys)
+ body = self.admin_volume_qos_client.show_qos(
self.created_qos['id'])['qos_specs']
self.assertNotIn(keys[0], body['specs'])
@@ -158,21 +159,20 @@
self.assertIn(vol_type[i]['id'], associations)
# disassociate a volume-type with qos-specs
- self.volume_qos_client.disassociate_qos(
+ self.admin_volume_qos_client.disassociate_qos(
self.created_qos['id'], vol_type[0]['id'])
operation = 'disassociate'
- self.volume_qos_client.wait_for_qos_operations(self.created_qos['id'],
- operation,
- vol_type[0]['id'])
+ self.admin_volume_qos_client.wait_for_qos_operations(
+ self.created_qos['id'], operation, vol_type[0]['id'])
associations = self._test_get_association_qos()
self.assertNotIn(vol_type[0]['id'], associations)
# disassociate all volume-types from qos-specs
- self.volume_qos_client.disassociate_all_qos(
+ self.admin_volume_qos_client.disassociate_all_qos(
self.created_qos['id'])
operation = 'disassociate-all'
- self.volume_qos_client.wait_for_qos_operations(self.created_qos['id'],
- operation)
+ self.admin_volume_qos_client.wait_for_qos_operations(
+ self.created_qos['id'], operation)
associations = self._test_get_association_qos()
self.assertEmpty(associations)
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 26a5a45..a17cc69 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -61,8 +61,7 @@
cls.client.wait_for_resource_deletion(cls.snapshot['id'])
# Delete the test volume
- cls.volumes_client.delete_volume(cls.volume['id'])
- cls.volumes_client.wait_for_resource_deletion(cls.volume['id'])
+ cls.delete_volume(cls.volumes_client, cls.volume['id'])
super(SnapshotsActionsV2Test, cls).resource_cleanup()
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index b28488a..b58c525 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -21,7 +21,7 @@
@test.idempotent_id('d5f3efa2-6684-4190-9ced-1c2f526352ad')
def test_list_hosts(self):
- hosts = self.hosts_client.list_hosts()['hosts']
+ hosts = self.admin_hosts_client.list_hosts()['hosts']
self.assertTrue(len(hosts) >= 2, "No. of hosts are < 2,"
"response of list hosts is: % s" % hosts)
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index cf05f5f..ba17d9c 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -13,9 +13,9 @@
# under the License.
import six
-
from tempest.api.volume import base
from tempest.common.utils import data_utils
+from tempest.common import waiters
from tempest import test
QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes']
@@ -25,26 +25,24 @@
class BaseVolumeQuotasAdminV2TestJSON(base.BaseVolumeAdminTest):
force_tenant_isolation = True
+ credentials = ['primary', 'alt', 'admin']
+
@classmethod
def setup_credentials(cls):
super(BaseVolumeQuotasAdminV2TestJSON, cls).setup_credentials()
cls.demo_tenant_id = cls.os.credentials.tenant_id
-
- def _delete_volume(self, volume_id):
- # Delete the specified volume using admin credentials
- self.admin_volume_client.delete_volume(volume_id)
- self.admin_volume_client.wait_for_resource_deletion(volume_id)
+ cls.alt_client = cls.os_alt.volumes_client
@test.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
def test_list_quotas(self):
- quotas = (self.quotas_client.show_quota_set(self.demo_tenant_id)
+ quotas = (self.admin_quotas_client.show_quota_set(self.demo_tenant_id)
['quota_set'])
for key in QUOTA_KEYS:
self.assertIn(key, quotas)
@test.idempotent_id('2be020a2-5fdd-423d-8d35-a7ffbc36e9f7')
def test_list_default_quotas(self):
- quotas = self.quotas_client.show_default_quota_set(
+ quotas = self.admin_quotas_client.show_default_quota_set(
self.demo_tenant_id)['quota_set']
for key in QUOTA_KEYS:
self.assertIn(key, quotas)
@@ -52,21 +50,21 @@
@test.idempotent_id('3d45c99e-cc42-4424-a56e-5cbd212b63a6')
def test_update_all_quota_resources_for_tenant(self):
# Admin can update all the resource quota limits for a tenant
- default_quota_set = self.quotas_client.show_default_quota_set(
+ default_quota_set = self.admin_quotas_client.show_default_quota_set(
self.demo_tenant_id)['quota_set']
new_quota_set = {'gigabytes': 1009,
'volumes': 11,
'snapshots': 11}
# Update limits for all quota resources
- quota_set = self.quotas_client.update_quota_set(
+ quota_set = self.admin_quotas_client.update_quota_set(
self.demo_tenant_id,
**new_quota_set)['quota_set']
cleanup_quota_set = dict(
(k, v) for k, v in six.iteritems(default_quota_set)
if k in QUOTA_KEYS)
- self.addCleanup(self.quotas_client.update_quota_set,
+ self.addCleanup(self.admin_quotas_client.update_quota_set,
self.demo_tenant_id, **cleanup_quota_set)
# test that the specific values we set are actually in
# the final result. There is nothing here that ensures there
@@ -75,7 +73,7 @@
@test.idempotent_id('18c51ae9-cb03-48fc-b234-14a19374dbed')
def test_show_quota_usage(self):
- quota_usage = self.quotas_client.show_quota_usage(
+ quota_usage = self.admin_quotas_client.show_quota_usage(
self.os_adm.credentials.tenant_id)['quota_set']
for key in QUOTA_KEYS:
self.assertIn(key, quota_usage)
@@ -84,13 +82,14 @@
@test.idempotent_id('ae8b6091-48ad-4bfa-a188-bbf5cc02115f')
def test_quota_usage(self):
- quota_usage = self.quotas_client.show_quota_usage(
+ quota_usage = self.admin_quotas_client.show_quota_usage(
self.demo_tenant_id)['quota_set']
volume = self.create_volume()
- self.addCleanup(self._delete_volume, volume['id'])
+ self.addCleanup(self.delete_volume,
+ self.admin_volume_client, volume['id'])
- new_quota_usage = self.quotas_client.show_quota_usage(
+ new_quota_usage = self.admin_quotas_client.show_quota_usage(
self.demo_tenant_id)['quota_set']
self.assertEqual(quota_usage['volumes']['in_use'] + 1,
@@ -109,18 +108,67 @@
description=description)
project_id = project['id']
self.addCleanup(self.identity_utils.delete_project, project_id)
- quota_set_default = self.quotas_client.show_default_quota_set(
+ quota_set_default = self.admin_quotas_client.show_default_quota_set(
project_id)['quota_set']
volume_default = quota_set_default['volumes']
- self.quotas_client.update_quota_set(project_id,
- volumes=(int(volume_default) + 5))
+ self.admin_quotas_client.update_quota_set(
+ project_id, volumes=(int(volume_default) + 5))
- self.quotas_client.delete_quota_set(project_id)
- quota_set_new = (self.quotas_client.show_quota_set(project_id)
+ self.admin_quotas_client.delete_quota_set(project_id)
+ quota_set_new = (self.admin_quotas_client.show_quota_set(project_id)
['quota_set'])
self.assertEqual(volume_default, quota_set_new['volumes'])
+ @test.idempotent_id('8911036f-9d54-4720-80cc-a1c9796a8805')
+ def test_quota_usage_after_volume_transfer(self):
+ # Create a volume for transfer
+ volume = self.create_volume()
+ self.addCleanup(self.delete_volume,
+ self.admin_volume_client, volume['id'])
+
+ # List of tenants quota usage pre-transfer
+ primary_quota = self.admin_quotas_client.show_quota_usage(
+ self.demo_tenant_id)['quota_set']
+
+ alt_quota = self.admin_quotas_client.show_quota_usage(
+ self.alt_client.tenant_id)['quota_set']
+
+ # Creates a volume transfer
+ transfer = self.volumes_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(
+ transfer_id, auth_key=auth_key)['transfer']
+
+ # Verify volume transferred is available
+ waiters.wait_for_volume_status(
+ self.alt_client, volume['id'], 'available')
+
+ # List of tenants quota usage post transfer
+ new_primary_quota = self.admin_quotas_client.show_quota_usage(
+ self.demo_tenant_id)['quota_set']
+
+ new_alt_quota = self.admin_quotas_client.show_quota_usage(
+ self.alt_client.tenant_id)['quota_set']
+
+ # Verify tenants quota usage was updated
+ self.assertEqual(primary_quota['volumes']['in_use'] -
+ new_primary_quota['volumes']['in_use'],
+ new_alt_quota['volumes']['in_use'] -
+ alt_quota['volumes']['in_use'])
+
+ self.assertEqual(alt_quota['gigabytes']['in_use'] +
+ volume['size'],
+ new_alt_quota['gigabytes']['in_use'])
+
+ 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 a43ee8e..dde8915 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -38,7 +38,7 @@
# NOTE(gfidente): no need to restore original quota set
# after the tests as they only work with dynamic credentials.
- cls.quotas_client.update_quota_set(
+ cls.admin_quotas_client.update_quota_set(
cls.demo_tenant_id,
**cls.shared_quota_set)
@@ -58,12 +58,12 @@
# NOTE(gfidente): quota set needs to be changed for this test
# or we may be limited by the volumes or snaps quota number, not by
# actual gigs usage; next line ensures shared set is restored.
- self.addCleanup(self.quotas_client.update_quota_set,
+ self.addCleanup(self.admin_quotas_client.update_quota_set,
self.demo_tenant_id,
**self.shared_quota_set)
new_quota_set = {'gigabytes': self.default_volume_size,
'volumes': 2, 'snapshots': 1}
- self.quotas_client.update_quota_set(
+ self.admin_quotas_client.update_quota_set(
self.demo_tenant_id,
**new_quota_set)
self.assertRaises(lib_exc.OverLimit,
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 b7f70ba..1565a8c 100644
--- a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
@@ -44,7 +44,7 @@
# NOTE(gfidente): no need to restore original quota set
# after the tests as they only work with tenant isolation.
- cls.quotas_client.update_quota_set(
+ cls.admin_quotas_client.update_quota_set(
cls.demo_tenant_id,
**cls.shared_quota_set)
@@ -63,12 +63,12 @@
@test.attr(type='negative')
@test.idempotent_id('c99a1ca9-6cdf-498d-9fdf-25832babef27')
def test_quota_volume_gigabytes_snapshots(self):
- self.addCleanup(self.quotas_client.update_quota_set,
+ self.addCleanup(self.admin_quotas_client.update_quota_set,
self.demo_tenant_id,
**self.shared_quota_set)
new_quota_set = {'gigabytes': 2 * self.default_volume_size,
'volumes': 1, 'snapshots': 2}
- self.quotas_client.update_quota_set(
+ self.admin_quotas_client.update_quota_set(
self.demo_tenant_id,
**new_quota_set)
self.assertRaises(lib_exc.OverLimit,
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 6fc6f1c..9023037 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -24,14 +24,11 @@
class VolumeTypesV2Test(base.BaseVolumeAdminTest):
- def _delete_volume(self, volume_id):
- self.volumes_client.delete_volume(volume_id)
- self.volumes_client.wait_for_resource_deletion(volume_id)
-
@test.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
def test_volume_type_list(self):
# List volume types.
- body = self.volume_types_client.list_volume_types()['volume_types']
+ body = \
+ self.admin_volume_types_client.list_volume_types()['volume_types']
self.assertIsInstance(body, list)
@test.idempotent_id('c03cc62c-f4e9-4623-91ec-64ce2f9c1260')
@@ -47,18 +44,18 @@
# Create two volume_types
for i in range(2):
vol_type_name = data_utils.rand_name("volume-type")
- vol_type = self.volume_types_client.create_volume_type(
+ vol_type = self.admin_volume_types_client.create_volume_type(
name=vol_type_name,
extra_specs=extra_specs)['volume_type']
volume_types.append(vol_type)
- self.addCleanup(self.volume_types_client.delete_volume_type,
+ self.addCleanup(self.admin_volume_types_client.delete_volume_type,
vol_type['id'])
params = {self.name_field: vol_name,
'volume_type': volume_types[0]['id']}
# Create volume
volume = self.volumes_client.create_volume(**params)['volume']
- self.addCleanup(self._delete_volume, volume['id'])
+ self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
self.assertEqual(volume_types[0]['name'], volume["volume_type"])
self.assertEqual(volume[self.name_field], vol_name,
"The created volume name is not equal "
@@ -97,11 +94,11 @@
vendor = CONF.volume.vendor_name
extra_specs = {"storage_protocol": proto,
"vendor_name": vendor}
- body = self.volume_types_client.create_volume_type(
+ body = self.admin_volume_types_client.create_volume_type(
name=name,
extra_specs=extra_specs)['volume_type']
self.assertIn('id', body)
- self.addCleanup(self.volume_types_client.delete_volume_type,
+ self.addCleanup(self.admin_volume_types_client.delete_volume_type,
body['id'])
self.assertIn('name', body)
self.assertEqual(body['name'], name,
@@ -109,7 +106,7 @@
"to the requested name")
self.assertTrue(body['id'] is not None,
"Field volume_type id is empty or not found.")
- fetched_volume_type = self.volume_types_client.show_volume_type(
+ fetched_volume_type = self.admin_volume_types_client.show_volume_type(
body['id'])['volume_type']
self.assertEqual(name, fetched_volume_type['name'],
'The fetched Volume_type is different '
@@ -127,15 +124,16 @@
provider = "LuksEncryptor"
control_location = "front-end"
name = data_utils.rand_name("volume-type")
- body = self.volume_types_client.create_volume_type(
+ body = self.admin_volume_types_client.create_volume_type(
name=name)['volume_type']
- self.addCleanup(self.volume_types_client.delete_volume_type,
+ self.addCleanup(self.admin_volume_types_client.delete_volume_type,
body['id'])
# Create encryption type
- encryption_type = self.volume_types_client.create_encryption_type(
- body['id'], provider=provider,
- control_location=control_location)['encryption']
+ encryption_type = \
+ self.admin_volume_types_client.create_encryption_type(
+ body['id'], provider=provider,
+ control_location=control_location)['encryption']
self.assertIn('volume_type_id', encryption_type)
self.assertEqual(provider, encryption_type['provider'],
"The created encryption_type provider is not equal "
@@ -146,7 +144,7 @@
# Get encryption type
fetched_encryption_type = (
- self.volume_types_client.show_encryption_type(
+ self.admin_volume_types_client.show_encryption_type(
encryption_type['volume_type_id']))
self.assertEqual(provider,
fetched_encryption_type['provider'],
@@ -158,13 +156,13 @@
'different from the created encryption_type')
# Delete encryption type
- self.volume_types_client.delete_encryption_type(
+ self.admin_volume_types_client.delete_encryption_type(
encryption_type['volume_type_id'])
resource = {"id": encryption_type['volume_type_id'],
"type": "encryption-type"}
- self.volume_types_client.wait_for_resource_deletion(resource)
+ self.admin_volume_types_client.wait_for_resource_deletion(resource)
deleted_encryption_type = (
- self.volume_types_client.show_encryption_type(
+ self.admin_volume_types_client.show_encryption_type(
encryption_type['volume_type_id']))
self.assertEmpty(deleted_encryption_type)
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 502cd86..b5c5d15 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -24,23 +24,24 @@
def resource_setup(cls):
super(VolumeTypesExtraSpecsV2Test, cls).resource_setup()
vol_type_name = data_utils.rand_name('Volume-type')
- cls.volume_type = cls.volume_types_client.create_volume_type(
- name=vol_type_name)['volume_type']
+ cls.volume_type = \
+ cls.admin_volume_types_client.create_volume_type(
+ name=vol_type_name)['volume_type']
@classmethod
def resource_cleanup(cls):
- cls.volume_types_client.delete_volume_type(cls.volume_type['id'])
+ cls.admin_volume_types_client.delete_volume_type(cls.volume_type['id'])
super(VolumeTypesExtraSpecsV2Test, cls).resource_cleanup()
@test.idempotent_id('b42923e9-0452-4945-be5b-d362ae533e60')
def test_volume_type_extra_specs_list(self):
# List Volume types extra specs.
extra_specs = {"spec1": "val1"}
- body = self.volume_types_client.create_volume_type_extra_specs(
+ body = self.admin_volume_types_client.create_volume_type_extra_specs(
self.volume_type['id'], extra_specs)['extra_specs']
self.assertEqual(extra_specs, body,
"Volume type extra spec incorrectly created")
- body = self.volume_types_client.list_volume_types_extra_specs(
+ body = self.admin_volume_types_client.list_volume_types_extra_specs(
self.volume_type['id'])['extra_specs']
self.assertIsInstance(body, dict)
self.assertIn('spec1', body)
@@ -49,39 +50,36 @@
def test_volume_type_extra_specs_update(self):
# Update volume type extra specs
extra_specs = {"spec2": "val1"}
- body = self.volume_types_client.create_volume_type_extra_specs(
+ body = self.admin_volume_types_client.create_volume_type_extra_specs(
self.volume_type['id'], extra_specs)['extra_specs']
self.assertEqual(extra_specs, body,
"Volume type extra spec incorrectly created")
-
- extra_spec = {"spec2": "val2"}
- body = self.volume_types_client.update_volume_type_extra_specs(
- self.volume_type['id'],
- extra_spec.keys()[0],
- extra_spec)
- self.assertIn('spec2', body)
- self.assertEqual(extra_spec['spec2'], body['spec2'],
+ spec_key = "spec2"
+ extra_spec = {spec_key: "val2"}
+ body = self.admin_volume_types_client.update_volume_type_extra_specs(
+ self.volume_type['id'], spec_key, extra_spec)
+ self.assertIn(spec_key, body)
+ self.assertEqual(extra_spec[spec_key], body[spec_key],
"Volume type extra spec incorrectly updated")
@test.idempotent_id('d4772798-601f-408a-b2a5-29e8a59d1220')
def test_volume_type_extra_spec_create_get_delete(self):
# Create/Get/Delete volume type extra spec.
- extra_specs = {"spec3": "val1"}
- body = self.volume_types_client.create_volume_type_extra_specs(
+ spec_key = "spec3"
+ extra_specs = {spec_key: "val1"}
+ body = self.admin_volume_types_client.create_volume_type_extra_specs(
self.volume_type['id'],
extra_specs)['extra_specs']
self.assertEqual(extra_specs, body,
"Volume type extra spec incorrectly created")
- self.volume_types_client.show_volume_type_extra_specs(
+ self.admin_volume_types_client.show_volume_type_extra_specs(
self.volume_type['id'],
- extra_specs.keys()[0])
+ spec_key)
self.assertEqual(extra_specs, body,
"Volume type extra spec incorrectly fetched")
-
- self.volume_types_client.delete_volume_type_extra_specs(
- self.volume_type['id'],
- extra_specs.keys()[0])
+ self.admin_volume_types_client.delete_volume_type_extra_specs(
+ self.volume_type['id'], spec_key)
class VolumeTypesExtraSpecsV1Test(VolumeTypesExtraSpecsV2Test):
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 f3e52e9..c7c3e1a 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
@@ -26,23 +26,22 @@
super(ExtraSpecsNegativeV2Test, cls).resource_setup()
vol_type_name = data_utils.rand_name('Volume-type')
cls.extra_specs = {"spec1": "val1"}
- cls.volume_type = cls.volume_types_client.create_volume_type(
+ cls.volume_type = cls.admin_volume_types_client.create_volume_type(
name=vol_type_name,
extra_specs=cls.extra_specs)['volume_type']
@classmethod
def resource_cleanup(cls):
- cls.volume_types_client.delete_volume_type(cls.volume_type['id'])
+ cls.admin_volume_types_client.delete_volume_type(cls.volume_type['id'])
super(ExtraSpecsNegativeV2Test, cls).resource_cleanup()
@test.idempotent_id('08961d20-5cbb-4910-ac0f-89ad6dbb2da1')
def test_update_no_body(self):
# Should not update volume type extra specs with no body
- extra_spec = {"spec1": "val2"}
self.assertRaises(
lib_exc.BadRequest,
- self.volume_types_client.update_volume_type_extra_specs,
- self.volume_type['id'], extra_spec.keys()[0], None)
+ self.admin_volume_types_client.update_volume_type_extra_specs,
+ self.volume_type['id'], "spec1", None)
@test.idempotent_id('25e5a0ee-89b3-4c53-8310-236f76c75365')
def test_update_nonexistent_extra_spec_id(self):
@@ -50,7 +49,7 @@
extra_spec = {"spec1": "val2"}
self.assertRaises(
lib_exc.BadRequest,
- self.volume_types_client.update_volume_type_extra_specs,
+ self.admin_volume_types_client.update_volume_type_extra_specs,
self.volume_type['id'], data_utils.rand_uuid(),
extra_spec)
@@ -60,7 +59,7 @@
extra_spec = {"spec1": "val2"}
self.assertRaises(
lib_exc.BadRequest,
- self.volume_types_client.update_volume_type_extra_specs,
+ self.admin_volume_types_client.update_volume_type_extra_specs,
self.volume_type['id'], None, extra_spec)
@test.idempotent_id('a77dfda2-9100-448e-9076-ed1711f4bdfc')
@@ -70,8 +69,8 @@
extra_spec = {"spec1": "val2", "spec2": "val1"}
self.assertRaises(
lib_exc.BadRequest,
- self.volume_types_client.update_volume_type_extra_specs,
- self.volume_type['id'], extra_spec.keys()[0],
+ self.admin_volume_types_client.update_volume_type_extra_specs,
+ self.volume_type['id'], list(extra_spec)[0],
extra_spec)
@test.idempotent_id('49d5472c-a53d-4eab-a4d3-450c4db1c545')
@@ -81,7 +80,7 @@
extra_specs = {"spec2": "val1"}
self.assertRaises(
lib_exc.NotFound,
- self.volume_types_client.create_volume_type_extra_specs,
+ self.admin_volume_types_client.create_volume_type_extra_specs,
data_utils.rand_uuid(), extra_specs)
@test.idempotent_id('c821bdc8-43a4-4bf4-86c8-82f3858d5f7d')
@@ -89,7 +88,7 @@
# Should not create volume type extra spec for none POST body.
self.assertRaises(
lib_exc.BadRequest,
- self.volume_types_client.create_volume_type_extra_specs,
+ self.admin_volume_types_client.create_volume_type_extra_specs,
self.volume_type['id'], None)
@test.idempotent_id('bc772c71-1ed4-4716-b945-8b5ed0f15e87')
@@ -97,35 +96,33 @@
# Should not create volume type extra spec for invalid POST body.
self.assertRaises(
lib_exc.BadRequest,
- self.volume_types_client.create_volume_type_extra_specs,
+ self.admin_volume_types_client.create_volume_type_extra_specs,
self.volume_type['id'], extra_specs=['invalid'])
@test.idempotent_id('031cda8b-7d23-4246-8bf6-bbe73fd67074')
def test_delete_nonexistent_volume_type_id(self):
# Should not delete volume type extra spec for nonexistent
- # type id.
- extra_specs = {"spec1": "val1"}
+ # type id.
self.assertRaises(
lib_exc.NotFound,
- self.volume_types_client.delete_volume_type_extra_specs,
- data_utils.rand_uuid(), extra_specs.keys()[0])
+ self.admin_volume_types_client.delete_volume_type_extra_specs,
+ data_utils.rand_uuid(), "spec1")
@test.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.
self.assertRaises(
lib_exc.NotFound,
- self.volume_types_client.list_volume_types_extra_specs,
+ self.admin_volume_types_client.list_volume_types_extra_specs,
data_utils.rand_uuid())
@test.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.
- extra_specs = {"spec1": "val1"}
self.assertRaises(
lib_exc.NotFound,
- self.volume_types_client.show_volume_type_extra_specs,
- data_utils.rand_uuid(), extra_specs.keys()[0])
+ self.admin_volume_types_client.show_volume_type_extra_specs,
+ data_utils.rand_uuid(), "spec1")
@test.idempotent_id('c881797d-12ff-4f1a-b09d-9f6212159753')
def test_get_nonexistent_extra_spec_id(self):
@@ -133,7 +130,7 @@
# id.
self.assertRaises(
lib_exc.NotFound,
- self.volume_types_client.show_volume_type_extra_specs,
+ self.admin_volume_types_client.show_volume_type_extra_specs,
self.volume_type['id'], data_utils.rand_uuid())
diff --git a/tempest/api/volume/admin/test_volume_types_negative.py b/tempest/api/volume/admin/test_volume_types_negative.py
index aff5466..857e7d2 100644
--- a/tempest/api/volume/admin/test_volume_types_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_negative.py
@@ -33,21 +33,22 @@
@test.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.
- self.assertRaises(lib_exc.BadRequest,
- self.volume_types_client.create_volume_type, name='')
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.admin_volume_types_client.create_volume_type, name='')
@test.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.
self.assertRaises(lib_exc.NotFound,
- self.volume_types_client.show_volume_type,
+ self.admin_volume_types_client.show_volume_type,
data_utils.rand_uuid())
@test.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.
self.assertRaises(lib_exc.NotFound,
- self.volume_types_client.delete_volume_type,
+ self.admin_volume_types_client.delete_volume_type,
data_utils.rand_uuid())
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index bdb313f..5388f7f 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -42,8 +42,7 @@
@classmethod
def resource_cleanup(cls):
# Delete the test volume
- cls.client.delete_volume(cls.volume['id'])
- cls.client.wait_for_resource_deletion(cls.volume['id'])
+ cls.delete_volume(cls.client, cls.volume['id'])
super(VolumesActionsV2Test, cls).resource_cleanup()
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 1289297..66bab51 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -42,8 +42,8 @@
cls.volume = cls.create_volume()
def _delete_backup(self, backup_id):
- self.backups_adm_client.delete_backup(backup_id)
- self.backups_adm_client.wait_for_backup_deletion(backup_id)
+ self.admin_backups_client.delete_backup(backup_id)
+ self.admin_backups_client.wait_for_backup_deletion(backup_id)
def _decode_url(self, backup_url):
return json.loads(base64.decodestring(backup_url))
@@ -63,36 +63,37 @@
def test_volume_backup_create_get_detailed_list_restore_delete(self):
# Create backup
backup_name = data_utils.rand_name('Backup')
- create_backup = self.backups_adm_client.create_backup
+ create_backup = self.admin_backups_client.create_backup
backup = create_backup(volume_id=self.volume['id'],
name=backup_name)['backup']
- self.addCleanup(self.backups_adm_client.delete_backup,
+ self.addCleanup(self.admin_backups_client.delete_backup,
backup['id'])
self.assertEqual(backup_name, backup['name'])
waiters.wait_for_volume_status(self.admin_volume_client,
self.volume['id'], 'available')
- self.backups_adm_client.wait_for_backup_status(backup['id'],
- 'available')
+ self.admin_backups_client.wait_for_backup_status(backup['id'],
+ 'available')
# Get a given backup
- backup = self.backups_adm_client.show_backup(backup['id'])['backup']
+ backup = self.admin_backups_client.show_backup(backup['id'])['backup']
self.assertEqual(backup_name, backup['name'])
# Get all backups with detail
- backups = self.backups_adm_client.list_backups(detail=True)['backups']
+ backups = self.admin_backups_client.list_backups(
+ detail=True)['backups']
self.assertIn((backup['name'], backup['id']),
[(m['name'], m['id']) for m in backups])
# Restore backup
- restore = self.backups_adm_client.restore_backup(
+ restore = self.admin_backups_client.restore_backup(
backup['id'])['restore']
# Delete backup
self.addCleanup(self.admin_volume_client.delete_volume,
restore['volume_id'])
self.assertEqual(backup['id'], restore['backup_id'])
- self.backups_adm_client.wait_for_backup_status(backup['id'],
- 'available')
+ self.admin_backups_client.wait_for_backup_status(backup['id'],
+ 'available')
waiters.wait_for_volume_status(self.admin_volume_client,
restore['volume_id'], 'available')
@@ -105,15 +106,15 @@
"""
# Create backup
backup_name = data_utils.rand_name('Backup')
- backup = (self.backups_adm_client.create_backup(
+ backup = (self.admin_backups_client.create_backup(
volume_id=self.volume['id'], name=backup_name)['backup'])
self.addCleanup(self._delete_backup, backup['id'])
self.assertEqual(backup_name, backup['name'])
- self.backups_adm_client.wait_for_backup_status(backup['id'],
- 'available')
+ self.admin_backups_client.wait_for_backup_status(backup['id'],
+ 'available')
# Export Backup
- export_backup = (self.backups_adm_client.export_backup(backup['id'])
+ export_backup = (self.admin_backups_client.export_backup(backup['id'])
['backup-record'])
self.assertIn('backup_service', export_backup)
self.assertIn('backup_url', export_backup)
@@ -132,7 +133,7 @@
export_backup['backup_url'], {'id': new_id})
# Import Backup
- import_backup = self.backups_adm_client.import_backup(
+ import_backup = self.admin_backups_client.import_backup(
backup_service=export_backup['backup_service'],
backup_url=new_url)['backup']
@@ -142,15 +143,16 @@
self.addCleanup(self._delete_backup, new_id)
self.assertIn("id", import_backup)
self.assertEqual(new_id, import_backup['id'])
- self.backups_adm_client.wait_for_backup_status(import_backup['id'],
- 'available')
+ self.admin_backups_client.wait_for_backup_status(import_backup['id'],
+ 'available')
# Verify Import Backup
- backups = self.backups_adm_client.list_backups(detail=True)['backups']
+ backups = self.admin_backups_client.list_backups(
+ detail=True)['backups']
self.assertIn(new_id, [b['id'] for b in backups])
# Restore backup
- restore = self.backups_adm_client.restore_backup(
+ restore = self.admin_backups_client.restore_backup(
backup['id'])['restore']
self.addCleanup(self.admin_volume_client.delete_volume,
restore['volume_id'])
@@ -161,8 +163,8 @@
# Verify if restored volume is there in volume list
volumes = self.admin_volume_client.list_volumes()['volumes']
self.assertIn(restore['volume_id'], [v['id'] for v in volumes])
- self.backups_adm_client.wait_for_backup_status(import_backup['id'],
- 'available')
+ self.admin_backups_client.wait_for_backup_status(import_backup['id'],
+ 'available')
class VolumesBackupsV1Test(VolumesBackupsV2Test):
diff --git a/tempest/api/volume/admin/test_volumes_list.py b/tempest/api/volume/admin/test_volumes_list.py
new file mode 100644
index 0000000..64041b8
--- /dev/null
+++ b/tempest/api/volume/admin/test_volumes_list.py
@@ -0,0 +1,59 @@
+# 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
+
+from tempest.api.volume import base
+from tempest.common import waiters
+from tempest import test
+
+
+class VolumesListAdminV2TestJSON(base.BaseVolumeAdminTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(VolumesListAdminV2TestJSON, cls).resource_setup()
+ # Create 3 test volumes
+ cls.volume_list = []
+ for i in range(3):
+ volume = cls.create_volume()
+ # Fetch volume details
+ volume_details = cls.volumes_client.show_volume(
+ volume['id'])['volume']
+ cls.volume_list.append(volume_details)
+
+ @test.idempotent_id('5866286f-3290-4cfd-a414-088aa6cdc469')
+ def test_volume_list_param_tenant(self):
+ # Test to list volumes from single tenant
+ # Create a volume in admin tenant
+ adm_vol = self.admin_volume_client.create_volume()['volume']
+ waiters.wait_for_volume_status(self.admin_volume_client,
+ adm_vol['id'], 'available')
+ self.addCleanup(self.admin_volume_client.delete_volume, adm_vol['id'])
+ params = {'all_tenants': 1,
+ 'project_id': self.volumes_client.tenant_id}
+ # Getting volume list from primary tenant using admin credentials
+ fetched_list = self.admin_volume_client.list_volumes(
+ detail=True, params=params)['volumes']
+ # Verifying fetched volume ids list is related to primary tenant
+ fetched_list_ids = map(operator.itemgetter('id'), fetched_list)
+ expected_list_ids = map(operator.itemgetter('id'), self.volume_list)
+ self.assertEqual(sorted(expected_list_ids), sorted(fetched_list_ids))
+ # Verifying tenant id of volumes fetched list is related to
+ # primary tenant
+ fetched_tenant_id = map(operator.itemgetter(
+ 'os-vol-tenant-attr:tenant_id'), fetched_list)
+ expected_tenant_id = [self.volumes_client.tenant_id] * 3
+ self.assertEqual(expected_tenant_id, fetched_tenant_id)
diff --git a/tempest/api/volume/api_microversion_fixture.py b/tempest/api/volume/api_microversion_fixture.py
new file mode 100644
index 0000000..6817eaa
--- /dev/null
+++ b/tempest/api/volume/api_microversion_fixture.py
@@ -0,0 +1,30 @@
+#
+# 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 fixtures
+
+from tempest.services.volume.base import base_v3_client
+
+
+class APIMicroversionFixture(fixtures.Fixture):
+
+ def __init__(self, volume_microversion):
+ self.volume_microversion = volume_microversion
+
+ def _setUp(self):
+ super(APIMicroversionFixture, self)._setUp()
+ base_v3_client.VOLUME_MICROVERSION = self.volume_microversion
+ self.addCleanup(self._reset_volume_microversion)
+
+ def _reset_volume_microversion(self):
+ base_v3_client.VOLUME_MICROVERSION = None
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 6116d72..665036b 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -45,6 +45,10 @@
if not CONF.volume_feature_enabled.api_v2:
msg = "Volume API v2 is disabled"
raise cls.skipException(msg)
+ elif cls._api_version == 3:
+ if not CONF.volume_feature_enabled.api_v3:
+ msg = "Volume API v3 is disabled"
+ raise cls.skipException(msg)
else:
msg = ("Invalid Cinder API version (%s)" % cls._api_version)
raise exceptions.InvalidConfiguration(message=msg)
@@ -131,6 +135,12 @@
# only in a single location in the source, and could be more general.
@classmethod
+ def delete_volume(cls, client, volume_id):
+ """Delete volume by the given client"""
+ client.delete_volume(volume_id)
+ client.wait_for_resource_deletion(volume_id)
+
+ @classmethod
def clear_volumes(cls):
for volume in cls.volumes:
try:
@@ -179,25 +189,25 @@
super(BaseVolumeAdminTest, cls).setup_clients()
if cls._api_version == 1:
- cls.volume_qos_client = cls.os_adm.volume_qos_client
+ cls.admin_volume_qos_client = cls.os_adm.volume_qos_client
cls.admin_volume_services_client = \
cls.os_adm.volume_services_client
- cls.volume_types_client = cls.os_adm.volume_types_client
+ cls.admin_volume_types_client = cls.os_adm.volume_types_client
cls.admin_volume_client = cls.os_adm.volumes_client
- cls.hosts_client = cls.os_adm.volume_hosts_client
+ cls.admin_hosts_client = cls.os_adm.volume_hosts_client
cls.admin_snapshots_client = cls.os_adm.snapshots_client
- cls.backups_adm_client = cls.os_adm.backups_client
- cls.quotas_client = cls.os_adm.volume_quotas_client
+ cls.admin_backups_client = cls.os_adm.backups_client
+ cls.admin_quotas_client = cls.os_adm.volume_quotas_client
elif cls._api_version == 2:
- cls.volume_qos_client = cls.os_adm.volume_qos_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.volume_types_client = cls.os_adm.volume_types_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.hosts_client = cls.os_adm.volume_hosts_v2_client
+ cls.admin_hosts_client = cls.os_adm.volume_hosts_v2_client
cls.admin_snapshots_client = cls.os_adm.snapshots_v2_client
- cls.backups_adm_client = cls.os_adm.backups_v2_client
- cls.quotas_client = cls.os_adm.volume_quotas_v2_client
+ cls.admin_backups_client = cls.os_adm.backups_v2_client
+ cls.admin_quotas_client = cls.os_adm.volume_quotas_v2_client
@classmethod
def resource_setup(cls):
@@ -215,7 +225,7 @@
"""create a test Qos-Specs."""
name = name or data_utils.rand_name(cls.__name__ + '-QoS')
consumer = consumer or 'front-end'
- qos_specs = cls.volume_qos_client.create_qos(
+ qos_specs = cls.admin_volume_qos_client.create_qos(
name=name, consumer=consumer, **kwargs)['qos_specs']
cls.qos_specs.append(qos_specs['id'])
return qos_specs
@@ -224,8 +234,8 @@
def clear_qos_specs(cls):
for qos_id in cls.qos_specs:
test_utils.call_and_ignore_notfound_exc(
- cls.volume_qos_client.delete_qos, qos_id)
+ cls.admin_volume_qos_client.delete_qos, qos_id)
for qos_id in cls.qos_specs:
test_utils.call_and_ignore_notfound_exc(
- cls.volume_qos_client.wait_for_resource_deletion, qos_id)
+ cls.admin_volume_qos_client.wait_for_resource_deletion, qos_id)
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 866db3d..d138490 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -36,16 +36,11 @@
cls.alt_tenant_id = cls.alt_client.tenant_id
cls.adm_client = cls.os_adm.volumes_client
- def _delete_volume(self, volume_id):
- # Delete the specified volume using admin creds
- self.adm_client.delete_volume(volume_id)
- self.adm_client.wait_for_resource_deletion(volume_id)
-
@test.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, volume['id'])
+ self.addCleanup(self.delete_volume, self.adm_client, volume['id'])
# Create a volume transfer
transfer = self.client.create_volume_transfer(
@@ -74,7 +69,7 @@
def test_create_list_delete_volume_transfer(self):
# Create a volume first
volume = self.create_volume()
- self.addCleanup(self._delete_volume, volume['id'])
+ self.addCleanup(self.delete_volume, self.adm_client, volume['id'])
# Create a volume transfer
body = self.client.create_volume_transfer(
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 5d83bb0..e5fcdfe 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -39,10 +39,6 @@
cls.name_field = cls.special_fields['name_field']
cls.descrip_field = cls.special_fields['descrip_field']
- def _delete_volume(self, volume_id):
- self.client.delete_volume(volume_id)
- self.client.wait_for_resource_deletion(volume_id)
-
def _volume_create_get_update_delete(self, **kwargs):
# Create a volume, Get it's details and Delete the volume
volume = {}
@@ -53,7 +49,7 @@
kwargs['metadata'] = metadata
volume = self.client.create_volume(**kwargs)['volume']
self.assertIn('id', volume)
- self.addCleanup(self._delete_volume, volume['id'])
+ self.addCleanup(self.delete_volume, self.client, volume['id'])
waiters.wait_for_volume_status(self.client, volume['id'], 'available')
self.assertIn(self.name_field, volume)
self.assertEqual(volume[self.name_field], v_name,
@@ -113,7 +109,7 @@
'availability_zone': volume['availability_zone']}
new_volume = self.client.create_volume(**params)['volume']
self.assertIn('id', new_volume)
- self.addCleanup(self._delete_volume, new_volume['id'])
+ self.addCleanup(self.delete_volume, self.client, new_volume['id'])
waiters.wait_for_volume_status(self.client,
new_volume['id'], 'available')
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 38a5a80..a93025d 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -72,8 +72,7 @@
def resource_cleanup(cls):
# Delete the created volumes
for volid in cls.volume_id_list:
- cls.client.delete_volume(volid)
- cls.client.wait_for_resource_deletion(volid)
+ cls.delete_volume(cls.client, volid)
super(VolumesV2ListTestJSON, cls).resource_cleanup()
def _list_by_param_value_and_assert(self, params, with_detail=False):
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 6707121..0f7c4f6 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -186,8 +186,7 @@
snapshot_id=snapshot['id'])['volume']
waiters.wait_for_volume_status(self.volumes_client,
volume['id'], 'available')
- self.volumes_client.delete_volume(volume['id'])
- self.volumes_client.wait_for_resource_deletion(volume['id'])
+ self.delete_volume(self.volumes_client, volume['id'])
self.cleanup_snapshot(snapshot)
@test.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
diff --git a/tempest/api/volume/v2/test_image_metadata.py b/tempest/api/volume/v2/test_image_metadata.py
new file mode 100644
index 0000000..1e7bb30
--- /dev/null
+++ b/tempest/api/volume/v2/test_image_metadata.py
@@ -0,0 +1,64 @@
+# 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 testtools import matchers
+
+from tempest.api.volume import base
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class VolumesV2ImageMetadata(base.BaseVolumeTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(VolumesV2ImageMetadata, cls).resource_setup()
+ # Create a volume from image ID
+ cls.volume = cls.create_volume(imageRef=CONF.compute.image_ref)
+
+ @test.idempotent_id('03efff0b-5c75-4822-8f10-8789ac15b13e')
+ @test.services('image')
+ def test_update_image_metadata(self):
+ # Update image metadata
+ image_metadata = {'image_id': '5137a025-3c5f-43c1-bc64-5f41270040a5',
+ 'image_name': 'image',
+ 'kernel_id': '6ff710d2-942b-4d6b-9168-8c9cc2404ab1',
+ 'ramdisk_id': 'somedisk'}
+ self.volumes_client.update_volume_image_metadata(self.volume['id'],
+ **image_metadata)
+
+ # Fetch image metadata from the volume
+ volume_image_metadata = self.volumes_client.show_volume(
+ self.volume['id'])['volume']['volume_image_metadata']
+
+ # Verify image metadata was updated
+ self.assertThat(volume_image_metadata.items(),
+ matchers.ContainsAll(image_metadata.items()))
+
+ # Delete one item from image metadata of the volume
+ self.volumes_client.delete_volume_image_metadata(self.volume['id'],
+ 'ramdisk_id')
+ del image_metadata['ramdisk_id']
+
+ # Fetch the new image metadata from the volume
+ volume_image_metadata = self.volumes_client.show_volume(
+ self.volume['id'])['volume']['volume_image_metadata']
+
+ # Verify image metadata was updated after item deletion
+ self.assertThat(volume_image_metadata.items(),
+ matchers.ContainsAll(image_metadata.items()))
+ self.assertNotIn('ramdisk_id', volume_image_metadata)
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 1fa54c2..5117e6c 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -55,8 +55,7 @@
def resource_cleanup(cls):
# Delete the created volumes
for volid in cls.volume_id_list:
- cls.client.delete_volume(volid)
- cls.client.wait_for_resource_deletion(volid)
+ cls.delete_volume(cls.client, volid)
super(VolumesV2ListTestJSON, cls).resource_cleanup()
@test.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
diff --git a/tempest/services/image/v2/__init__.py b/tempest/api/volume/v3/__init__.py
similarity index 100%
copy from tempest/services/image/v2/__init__.py
copy to tempest/api/volume/v3/__init__.py
diff --git a/tempest/services/image/v2/__init__.py b/tempest/api/volume/v3/admin/__init__.py
similarity index 100%
copy from tempest/services/image/v2/__init__.py
copy to tempest/api/volume/v3/admin/__init__.py
diff --git a/tempest/api/volume/v3/admin/test_user_messages.py b/tempest/api/volume/v3/admin/test_user_messages.py
new file mode 100644
index 0000000..9d59d1b
--- /dev/null
+++ b/tempest/api/volume/v3/admin/test_user_messages.py
@@ -0,0 +1,94 @@
+# 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.v3 import base
+from tempest.common.utils import data_utils
+from tempest.common import waiters
+from tempest import exceptions
+from tempest import test
+
+MESSAGE_KEYS = [
+ 'created_at',
+ 'event_id',
+ 'guaranteed_until',
+ 'id',
+ 'message_level',
+ 'request_id',
+ 'resource_type',
+ 'resource_uuid',
+ 'user_message',
+ 'links']
+
+
+class UserMessagesTest(base.VolumesV3AdminTest):
+ min_microversion = '3.3'
+ max_microversion = 'latest'
+
+ def _create_user_message(self):
+ """Trigger a 'no valid host' situation to generate a message."""
+ bad_protocol = data_utils.rand_name('storage_protocol')
+ bad_vendor = data_utils.rand_name('vendor_name')
+ extra_specs = {'storage_protocol': bad_protocol,
+ 'vendor_name': bad_vendor}
+ vol_type_name = data_utils.rand_name('volume-type')
+ bogus_type = self.admin_volume_types_client.create_volume_type(
+ name=vol_type_name,
+ extra_specs=extra_specs)['volume_type']
+ self.addCleanup(self.admin_volume_types_client.delete_volume_type,
+ bogus_type['id'])
+ params = {'volume_type': bogus_type['id']}
+ volume = self.volumes_client.create_volume(**params)['volume']
+ self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+ try:
+ waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+ 'error')
+ except exceptions.VolumeBuildErrorException:
+ # Error state is expected and desired
+ pass
+ messages = self.messages_client.list_messages()['messages']
+ message_id = None
+ for message in messages:
+ if message['resource_uuid'] == volume['id']:
+ message_id = message['id']
+ break
+ self.assertIsNotNone(message_id, 'No user message generated for '
+ 'volume %s' % volume['id'])
+ return message_id
+
+ @test.idempotent_id('50f29e6e-f363-42e1-8ad1-f67ae7fd4d5a')
+ def test_list_messages(self):
+ self._create_user_message()
+ messages = self.messages_client.list_messages()['messages']
+ self.assertIsInstance(messages, list)
+ for message in messages:
+ for key in MESSAGE_KEYS:
+ self.assertIn(key, message.keys(),
+ 'Missing expected key %s' % key)
+
+ @test.idempotent_id('55a4a61e-c7b2-4ba0-a05d-b914bdef3070')
+ def test_show_message(self):
+ message_id = self._create_user_message()
+ self.addCleanup(self.messages_client.delete_message, message_id)
+
+ message = self.messages_client.show_message(message_id)['message']
+
+ for key in MESSAGE_KEYS:
+ self.assertIn(key, message.keys(), 'Missing expected key %s' % key)
+
+ @test.idempotent_id('c6eb6901-cdcc-490f-b735-4fe251842aed')
+ def test_delete_message(self):
+ message_id = self._create_user_message()
+ self.messages_client.delete_message(message_id)
+ self.messages_client.wait_for_resource_deletion(message_id)
diff --git a/tempest/api/volume/v3/base.py b/tempest/api/volume/v3/base.py
new file mode 100644
index 0000000..c31c83c
--- /dev/null
+++ b/tempest/api/volume/v3/base.py
@@ -0,0 +1,64 @@
+# 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_messages_client
+
+ def setUp(self):
+ super(VolumesV3Test, self).setUp()
+ self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+ self.request_microversion))
+
+
+class VolumesV3AdminTest(VolumesV3Test):
+ """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_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 de9b88a..b7bc4fa 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -20,166 +20,17 @@
from tempest.common import negative_rest_client
from tempest import config
from tempest import exceptions
-from tempest.lib.services.compute.agents_client import AgentsClient
-from tempest.lib.services.compute.aggregates_client import AggregatesClient
-from tempest.lib.services.compute.availability_zone_client import \
- AvailabilityZoneClient
-from tempest.lib.services.compute.baremetal_nodes_client import \
- BaremetalNodesClient
-from tempest.lib.services.compute.certificates_client import \
- CertificatesClient
-from tempest.lib.services.compute.extensions_client import \
- ExtensionsClient
-from tempest.lib.services.compute.fixed_ips_client import FixedIPsClient
-from tempest.lib.services.compute.flavors_client import FlavorsClient
-from tempest.lib.services.compute.floating_ip_pools_client import \
- FloatingIPPoolsClient
-from tempest.lib.services.compute.floating_ips_bulk_client import \
- FloatingIPsBulkClient
-from tempest.lib.services.compute.floating_ips_client import \
- FloatingIPsClient as ComputeFloatingIPsClient
-from tempest.lib.services.compute.hosts_client import HostsClient
-from tempest.lib.services.compute.hypervisor_client import \
- HypervisorClient
-from tempest.lib.services.compute.images_client import ImagesClient \
- as ComputeImagesClient
-from tempest.lib.services.compute.instance_usage_audit_log_client import \
- InstanceUsagesAuditLogClient
-from tempest.lib.services.compute.interfaces_client import InterfacesClient
-from tempest.lib.services.compute.keypairs_client import KeyPairsClient
-from tempest.lib.services.compute.limits_client import LimitsClient
-from tempest.lib.services.compute.migrations_client import MigrationsClient
-from tempest.lib.services.compute.networks_client import NetworksClient \
- as ComputeNetworksClient
-from tempest.lib.services.compute.quota_classes_client import \
- QuotaClassesClient
-from tempest.lib.services.compute.quotas_client import QuotasClient
-from tempest.lib.services.compute.security_group_default_rules_client import \
- SecurityGroupDefaultRulesClient
-from tempest.lib.services.compute.security_group_rules_client import \
- SecurityGroupRulesClient as ComputeSecurityGroupRulesClient
-from tempest.lib.services.compute.security_groups_client import \
- SecurityGroupsClient as ComputeSecurityGroupsClient
-from tempest.lib.services.compute.server_groups_client import \
- ServerGroupsClient
-from tempest.lib.services.compute.servers_client import ServersClient
-from tempest.lib.services.compute.services_client import ServicesClient
-from tempest.lib.services.compute.snapshots_client import \
- SnapshotsClient as ComputeSnapshotsClient
-from tempest.lib.services.compute.tenant_networks_client import \
- TenantNetworksClient
-from tempest.lib.services.compute.tenant_usages_client import \
- TenantUsagesClient
-from tempest.lib.services.compute.versions_client import VersionsClient
-from tempest.lib.services.compute.volumes_client import \
- VolumesClient as ComputeVolumesClient
-from tempest.lib.services.identity.v2.token_client import TokenClient
-from tempest.lib.services.identity.v3.token_client import V3TokenClient
-from tempest.lib.services.network.agents_client import AgentsClient \
- as NetworkAgentsClient
-from tempest.lib.services.network.extensions_client import \
- ExtensionsClient as NetworkExtensionsClient
-from tempest.lib.services.network.floating_ips_client import FloatingIPsClient
-from tempest.lib.services.network.metering_label_rules_client import \
- MeteringLabelRulesClient
-from tempest.lib.services.network.metering_labels_client import \
- MeteringLabelsClient
-from tempest.lib.services.network.networks_client import NetworksClient
-from tempest.lib.services.network.ports_client import PortsClient
-from tempest.lib.services.network.quotas_client import QuotasClient \
- as NetworkQuotasClient
-from tempest.lib.services.network.routers_client import RoutersClient
-from tempest.lib.services.network.security_group_rules_client import \
- SecurityGroupRulesClient
-from tempest.lib.services.network.security_groups_client import \
- SecurityGroupsClient
-from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
-from tempest.lib.services.network.subnets_client import SubnetsClient
+from tempest.lib.services import compute
+from tempest.lib.services import network
from tempest import manager
-from tempest.services.baremetal.v1.json.baremetal_client import \
- BaremetalClient
-from tempest.services.data_processing.v1_1.data_processing_client import \
- DataProcessingClient
-from tempest.services.database.json.flavors_client import \
- DatabaseFlavorsClient
-from tempest.services.database.json.limits_client import \
- DatabaseLimitsClient
-from tempest.services.database.json.versions_client import \
- DatabaseVersionsClient
-from tempest.services.identity.v2.json.endpoints_client import EndpointsClient
-from tempest.services.identity.v2.json.identity_client import IdentityClient
-from tempest.services.identity.v2.json.roles_client import RolesClient
-from tempest.services.identity.v2.json.services_client import \
- ServicesClient as IdentityServicesClient
-from tempest.services.identity.v2.json.tenants_client import TenantsClient
-from tempest.services.identity.v2.json.users_client import UsersClient
-from tempest.services.identity.v3.json.credentials_client import \
- CredentialsClient
-from tempest.services.identity.v3.json.domains_client import DomainsClient
-from tempest.services.identity.v3.json.endpoints_client import \
- EndPointsClient as EndPointsV3Client
-from tempest.services.identity.v3.json.groups_client import GroupsClient
-from tempest.services.identity.v3.json.identity_client import \
- IdentityClient as IdentityV3Client
-from tempest.services.identity.v3.json.policies_client import PoliciesClient
-from tempest.services.identity.v3.json.projects_client import ProjectsClient
-from tempest.services.identity.v3.json.regions_client import RegionsClient
-from tempest.services.identity.v3.json.roles_client import \
- RolesClient as RolesV3Client
-from tempest.services.identity.v3.json.services_client import \
- ServicesClient as IdentityServicesV3Client
-from tempest.services.identity.v3.json.trusts_client import TrustsClient
-from tempest.services.identity.v3.json.users_clients import \
- UsersClient as UsersV3Client
-from tempest.services.image.v1.json.images_client import ImagesClient
-from tempest.services.image.v1.json.members_client import MembersClient
-from tempest.services.image.v2.json.images_client import \
- ImagesClient as ImagesV2Client
-from tempest.services.image.v2.json.members_client import MembersClient \
- as MembersClientV2
-from tempest.services.image.v2.json.namespaces_client import NamespacesClient
-from tempest.services.image.v2.json.schemas_client import SchemasClient
-from tempest.services.object_storage.account_client import AccountClient
-from tempest.services.object_storage.container_client import ContainerClient
-from tempest.services.object_storage.object_client import ObjectClient
-from tempest.services.orchestration.json.orchestration_client import \
- OrchestrationClient
-from tempest.services.volume.v1.json.admin.hosts_client import \
- HostsClient as VolumeHostsClient
-from tempest.services.volume.v1.json.admin.quotas_client import \
- QuotasClient as VolumeQuotasClient
-from tempest.services.volume.v1.json.admin.services_client import \
- ServicesClient as VolumeServicesClient
-from tempest.services.volume.v1.json.admin.types_client import \
- TypesClient as VolumeTypesClient
-from tempest.services.volume.v1.json.availability_zone_client import \
- AvailabilityZoneClient as VolumeAvailabilityZoneClient
-from tempest.services.volume.v1.json.backups_client import BackupsClient
-from tempest.services.volume.v1.json.extensions_client import \
- ExtensionsClient as VolumeExtensionsClient
-from tempest.services.volume.v1.json.qos_client import QosSpecsClient
-from tempest.services.volume.v1.json.snapshots_client import SnapshotsClient
-from tempest.services.volume.v1.json.volumes_client import VolumesClient
-from tempest.services.volume.v2.json.admin.hosts_client import \
- HostsClient as VolumeHostsV2Client
-from tempest.services.volume.v2.json.admin.quotas_client import \
- QuotasClient as VolumeQuotasV2Client
-from tempest.services.volume.v2.json.admin.services_client import \
- ServicesClient as VolumeServicesV2Client
-from tempest.services.volume.v2.json.admin.types_client import \
- TypesClient as VolumeTypesV2Client
-from tempest.services.volume.v2.json.availability_zone_client import \
- AvailabilityZoneClient as VolumeAvailabilityZoneV2Client
-from tempest.services.volume.v2.json.backups_client import \
- BackupsClient as BackupsV2Client
-from tempest.services.volume.v2.json.extensions_client import \
- ExtensionsClient as VolumeExtensionsV2Client
-from tempest.services.volume.v2.json.qos_client import \
- QosSpecsClient as QosSpecsV2Client
-from tempest.services.volume.v2.json.snapshots_client import \
- SnapshotsClient as SnapshotsV2Client
-from tempest.services.volume.v2.json.volumes_client import \
- VolumesClient as VolumesV2Client
+from tempest.services import baremetal
+from tempest.services import data_processing
+from tempest.services import database
+from tempest.services import identity
+from tempest.services import image
+from tempest.services import object_storage
+from tempest.services import orchestration
+from tempest.services import volume
CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -217,167 +68,16 @@
self._set_identity_clients()
self._set_volume_clients()
self._set_object_storage_clients()
+ self._set_image_clients()
+ self._set_network_clients()
- self.baremetal_client = BaremetalClient(
+ self.baremetal_client = baremetal.BaremetalClient(
self.auth_provider,
CONF.baremetal.catalog_type,
CONF.identity.region,
endpoint_type=CONF.baremetal.endpoint_type,
**self.default_params_with_timeout_values)
- self.network_agents_client = NetworkAgentsClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.network_extensions_client = NetworkExtensionsClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.networks_client = NetworksClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.subnetpools_client = SubnetpoolsClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.subnets_client = SubnetsClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.ports_client = PortsClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.network_quotas_client = NetworkQuotasClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.floating_ips_client = FloatingIPsClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.metering_labels_client = MeteringLabelsClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.metering_label_rules_client = MeteringLabelRulesClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.routers_client = RoutersClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.security_group_rules_client = SecurityGroupRulesClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- self.security_groups_client = SecurityGroupsClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
- if CONF.service_available.glance:
- self.image_client = ImagesClient(
- self.auth_provider,
- CONF.image.catalog_type,
- CONF.image.region or CONF.identity.region,
- endpoint_type=CONF.image.endpoint_type,
- build_interval=CONF.image.build_interval,
- build_timeout=CONF.image.build_timeout,
- **self.default_params)
- self.image_member_client = MembersClient(
- self.auth_provider,
- CONF.image.catalog_type,
- CONF.image.region or CONF.identity.region,
- endpoint_type=CONF.image.endpoint_type,
- build_interval=CONF.image.build_interval,
- build_timeout=CONF.image.build_timeout,
- **self.default_params)
- self.image_client_v2 = ImagesV2Client(
- self.auth_provider,
- CONF.image.catalog_type,
- CONF.image.region or CONF.identity.region,
- endpoint_type=CONF.image.endpoint_type,
- build_interval=CONF.image.build_interval,
- build_timeout=CONF.image.build_timeout,
- **self.default_params)
- self.image_member_client_v2 = MembersClientV2(
- self.auth_provider,
- CONF.image.catalog_type,
- CONF.image.region or CONF.identity.region,
- endpoint_type=CONF.image.endpoint_type,
- build_interval=CONF.image.build_interval,
- build_timeout=CONF.image.build_timeout,
- **self.default_params)
- self.namespaces_client = NamespacesClient(
- self.auth_provider,
- CONF.image.catalog_type,
- CONF.image.region or CONF.identity.region,
- endpoint_type=CONF.image.endpoint_type,
- build_interval=CONF.image.build_interval,
- build_timeout=CONF.image.build_timeout,
- **self.default_params)
- self.schemas_client = SchemasClient(
- self.auth_provider,
- CONF.image.catalog_type,
- CONF.image.region or CONF.identity.region,
- endpoint_type=CONF.image.endpoint_type,
- build_interval=CONF.image.build_interval,
- build_timeout=CONF.image.build_timeout,
- **self.default_params)
- self.orchestration_client = OrchestrationClient(
+ self.orchestration_client = orchestration.OrchestrationClient(
self.auth_provider,
CONF.orchestration.catalog_type,
CONF.orchestration.region or CONF.identity.region,
@@ -385,7 +85,7 @@
build_interval=CONF.orchestration.build_interval,
build_timeout=CONF.orchestration.build_timeout,
**self.default_params)
- self.data_processing_client = DataProcessingClient(
+ self.data_processing_client = data_processing.DataProcessingClient(
self.auth_provider,
CONF.data_processing.catalog_type,
CONF.identity.region,
@@ -394,6 +94,70 @@
self.negative_client = negative_rest_client.NegativeRestClient(
self.auth_provider, service, **self.default_params)
+ def _set_network_clients(self):
+ params = {
+ 'service': CONF.network.catalog_type,
+ 'region': CONF.network.region or CONF.identity.region,
+ 'endpoint_type': CONF.network.endpoint_type,
+ 'build_interval': CONF.network.build_interval,
+ 'build_timeout': CONF.network.build_timeout
+ }
+ params.update(self.default_params)
+ self.network_agents_client = network.AgentsClient(
+ self.auth_provider, **params)
+ self.network_extensions_client = network.ExtensionsClient(
+ self.auth_provider, **params)
+ self.networks_client = network.NetworksClient(
+ self.auth_provider, **params)
+ self.subnetpools_client = network.SubnetpoolsClient(
+ self.auth_provider, **params)
+ self.subnets_client = network.SubnetsClient(
+ self.auth_provider, **params)
+ self.ports_client = network.PortsClient(
+ self.auth_provider, **params)
+ self.network_quotas_client = network.QuotasClient(
+ self.auth_provider, **params)
+ self.floating_ips_client = network.FloatingIPsClient(
+ self.auth_provider, **params)
+ self.metering_labels_client = network.MeteringLabelsClient(
+ self.auth_provider, **params)
+ self.metering_label_rules_client = network.MeteringLabelRulesClient(
+ self.auth_provider, **params)
+ self.routers_client = network.RoutersClient(
+ self.auth_provider, **params)
+ self.security_group_rules_client = network.SecurityGroupRulesClient(
+ self.auth_provider, **params)
+ self.security_groups_client = network.SecurityGroupsClient(
+ self.auth_provider, **params)
+ self.network_versions_client = network.NetworkVersionsClient(
+ self.auth_provider, **params)
+
+ def _set_image_clients(self):
+ params = {
+ 'service': CONF.image.catalog_type,
+ 'region': CONF.image.region or CONF.identity.region,
+ 'endpoint_type': CONF.image.endpoint_type,
+ 'build_interval': CONF.image.build_interval,
+ 'build_timeout': CONF.image.build_timeout
+ }
+ params.update(self.default_params)
+
+ if CONF.service_available.glance:
+ self.image_client = image.v1.ImagesClient(
+ self.auth_provider, **params)
+ self.image_member_client = image.v1.ImageMembersClient(
+ self.auth_provider, **params)
+ self.image_client_v2 = image.v2.ImagesClient(
+ self.auth_provider, **params)
+ self.image_member_client_v2 = image.v2.ImageMembersClient(
+ self.auth_provider, **params)
+ self.namespaces_client = image.v2.NamespacesClient(
+ self.auth_provider, **params)
+ self.resource_types_client = image.v2.ResourceTypesClient(
+ self.auth_provider, **params)
+ self.schemas_client = image.v2.SchemasClient(
+ self.auth_provider, **params)
+
def _set_compute_clients(self):
params = {
'service': CONF.compute.catalog_type,
@@ -404,61 +168,65 @@
}
params.update(self.default_params)
- self.agents_client = AgentsClient(self.auth_provider, **params)
- self.compute_networks_client = ComputeNetworksClient(
+ self.agents_client = compute.AgentsClient(self.auth_provider, **params)
+ self.compute_networks_client = compute.NetworksClient(
self.auth_provider, **params)
- self.migrations_client = MigrationsClient(self.auth_provider,
- **params)
+ self.migrations_client = compute.MigrationsClient(self.auth_provider,
+ **params)
self.security_group_default_rules_client = (
- SecurityGroupDefaultRulesClient(self.auth_provider, **params))
- self.certificates_client = CertificatesClient(self.auth_provider,
- **params)
- self.servers_client = ServersClient(
+ compute.SecurityGroupDefaultRulesClient(self.auth_provider,
+ **params))
+ self.certificates_client = compute.CertificatesClient(
+ self.auth_provider, **params)
+ self.servers_client = compute.ServersClient(
self.auth_provider,
enable_instance_password=CONF.compute_feature_enabled
.enable_instance_password,
**params)
- self.server_groups_client = ServerGroupsClient(
+ self.server_groups_client = compute.ServerGroupsClient(
self.auth_provider, **params)
- self.limits_client = LimitsClient(self.auth_provider, **params)
- self.compute_images_client = ComputeImagesClient(self.auth_provider,
- **params)
- self.keypairs_client = KeyPairsClient(self.auth_provider, **params)
- self.quotas_client = QuotasClient(self.auth_provider, **params)
- self.quota_classes_client = QuotaClassesClient(self.auth_provider,
+ self.limits_client = compute.LimitsClient(self.auth_provider, **params)
+ self.compute_images_client = compute.ImagesClient(self.auth_provider,
+ **params)
+ self.keypairs_client = compute.KeyPairsClient(self.auth_provider,
+ **params)
+ self.quotas_client = compute.QuotasClient(self.auth_provider, **params)
+ self.quota_classes_client = compute.QuotaClassesClient(
+ self.auth_provider, **params)
+ self.flavors_client = compute.FlavorsClient(self.auth_provider,
+ **params)
+ self.extensions_client = compute.ExtensionsClient(self.auth_provider,
+ **params)
+ self.floating_ip_pools_client = compute.FloatingIPPoolsClient(
+ self.auth_provider, **params)
+ self.floating_ips_bulk_client = compute.FloatingIPsBulkClient(
+ self.auth_provider, **params)
+ self.compute_floating_ips_client = compute.FloatingIPsClient(
+ self.auth_provider, **params)
+ self.compute_security_group_rules_client = (
+ compute.SecurityGroupRulesClient(self.auth_provider, **params))
+ self.compute_security_groups_client = compute.SecurityGroupsClient(
+ self.auth_provider, **params)
+ self.interfaces_client = compute.InterfacesClient(self.auth_provider,
+ **params)
+ self.fixed_ips_client = compute.FixedIPsClient(self.auth_provider,
**params)
- self.flavors_client = FlavorsClient(self.auth_provider, **params)
- self.extensions_client = ExtensionsClient(self.auth_provider,
- **params)
- self.floating_ip_pools_client = FloatingIPPoolsClient(
+ self.availability_zone_client = compute.AvailabilityZoneClient(
self.auth_provider, **params)
- self.floating_ips_bulk_client = FloatingIPsBulkClient(
+ self.aggregates_client = compute.AggregatesClient(self.auth_provider,
+ **params)
+ self.services_client = compute.ServicesClient(self.auth_provider,
+ **params)
+ self.tenant_usages_client = compute.TenantUsagesClient(
self.auth_provider, **params)
- self.compute_floating_ips_client = ComputeFloatingIPsClient(
+ self.hosts_client = compute.HostsClient(self.auth_provider, **params)
+ self.hypervisor_client = compute.HypervisorClient(self.auth_provider,
+ **params)
+ self.instance_usages_audit_log_client = (
+ compute.InstanceUsagesAuditLogClient(self.auth_provider, **params))
+ self.tenant_networks_client = compute.TenantNetworksClient(
self.auth_provider, **params)
- self.compute_security_group_rules_client = \
- ComputeSecurityGroupRulesClient(self.auth_provider, **params)
- self.compute_security_groups_client = ComputeSecurityGroupsClient(
- self.auth_provider, **params)
- self.interfaces_client = InterfacesClient(self.auth_provider,
- **params)
- self.fixed_ips_client = FixedIPsClient(self.auth_provider,
- **params)
- self.availability_zone_client = AvailabilityZoneClient(
- self.auth_provider, **params)
- self.aggregates_client = AggregatesClient(self.auth_provider,
- **params)
- self.services_client = ServicesClient(self.auth_provider, **params)
- self.tenant_usages_client = TenantUsagesClient(self.auth_provider,
- **params)
- self.hosts_client = HostsClient(self.auth_provider, **params)
- self.hypervisor_client = HypervisorClient(self.auth_provider,
- **params)
- self.instance_usages_audit_log_client = \
- InstanceUsagesAuditLogClient(self.auth_provider, **params)
- self.tenant_networks_client = \
- TenantNetworksClient(self.auth_provider, **params)
- self.baremetal_nodes_client = BaremetalNodesClient(
+ self.baremetal_nodes_client = compute.BaremetalNodesClient(
self.auth_provider, **params)
# NOTE: The following client needs special timeout values because
@@ -468,25 +236,25 @@
'build_interval': CONF.volume.build_interval,
'build_timeout': CONF.volume.build_timeout
})
- self.volumes_extensions_client = ComputeVolumesClient(
+ self.volumes_extensions_client = compute.VolumesClient(
self.auth_provider, **params_volume)
- self.compute_versions_client = VersionsClient(self.auth_provider,
- **params_volume)
- self.snapshots_extensions_client = ComputeSnapshotsClient(
+ self.compute_versions_client = compute.VersionsClient(
+ self.auth_provider, **params_volume)
+ self.snapshots_extensions_client = compute.SnapshotsClient(
self.auth_provider, **params_volume)
def _set_database_clients(self):
- self.database_flavors_client = DatabaseFlavorsClient(
+ self.database_flavors_client = database.DatabaseFlavorsClient(
self.auth_provider,
CONF.database.catalog_type,
CONF.identity.region,
**self.default_params_with_timeout_values)
- self.database_limits_client = DatabaseLimitsClient(
+ self.database_limits_client = database.DatabaseLimitsClient(
self.auth_provider,
CONF.database.catalog_type,
CONF.identity.region,
**self.default_params_with_timeout_values)
- self.database_versions_client = DatabaseVersionsClient(
+ self.database_versions_client = database.DatabaseVersionsClient(
self.auth_provider,
CONF.database.catalog_type,
CONF.identity.region,
@@ -502,62 +270,71 @@
# Clients below use the admin endpoint type of Keystone API v2
params_v2_admin = params.copy()
params_v2_admin['endpoint_type'] = CONF.identity.v2_admin_endpoint_type
- self.endpoints_client = EndpointsClient(self.auth_provider,
- **params_v2_admin)
- self.identity_client = IdentityClient(self.auth_provider,
- **params_v2_admin)
- self.tenants_client = TenantsClient(self.auth_provider,
- **params_v2_admin)
- self.roles_client = RolesClient(self.auth_provider, **params_v2_admin)
- self.users_client = UsersClient(self.auth_provider, **params_v2_admin)
- self.identity_services_client = IdentityServicesClient(
+ self.endpoints_client = identity.v2.EndpointsClient(self.auth_provider,
+ **params_v2_admin)
+ self.identity_client = identity.v2.IdentityClient(self.auth_provider,
+ **params_v2_admin)
+ self.tenants_client = identity.v2.TenantsClient(self.auth_provider,
+ **params_v2_admin)
+ self.roles_client = identity.v2.RolesClient(self.auth_provider,
+ **params_v2_admin)
+ self.users_client = identity.v2.UsersClient(self.auth_provider,
+ **params_v2_admin)
+ self.identity_services_client = identity.v2.ServicesClient(
self.auth_provider, **params_v2_admin)
# Clients below use the public endpoint type of Keystone API v2
params_v2_public = params.copy()
params_v2_public['endpoint_type'] = (
CONF.identity.v2_public_endpoint_type)
- self.identity_public_client = IdentityClient(self.auth_provider,
- **params_v2_public)
- self.tenants_public_client = TenantsClient(self.auth_provider,
- **params_v2_public)
- self.users_public_client = UsersClient(self.auth_provider,
- **params_v2_public)
+ self.identity_public_client = identity.v2.IdentityClient(
+ self.auth_provider, **params_v2_public)
+ self.tenants_public_client = identity.v2.TenantsClient(
+ self.auth_provider, **params_v2_public)
+ self.users_public_client = identity.v2.UsersClient(
+ self.auth_provider, **params_v2_public)
# Clients below use the endpoint type of Keystone API v3
params_v3 = params.copy()
params_v3['endpoint_type'] = CONF.identity.v3_endpoint_type
- self.domains_client = DomainsClient(self.auth_provider,
- **params_v3)
- self.identity_v3_client = IdentityV3Client(self.auth_provider,
- **params_v3)
- self.trusts_client = TrustsClient(self.auth_provider, **params_v3)
- self.users_v3_client = UsersV3Client(self.auth_provider, **params_v3)
- self.endpoints_v3_client = EndPointsV3Client(self.auth_provider,
- **params_v3)
- self.roles_v3_client = RolesV3Client(self.auth_provider, **params_v3)
- self.identity_services_v3_client = IdentityServicesV3Client(
+ self.domains_client = identity.v3.DomainsClient(self.auth_provider,
+ **params_v3)
+ self.identity_v3_client = identity.v3.IdentityClient(
self.auth_provider, **params_v3)
- self.policies_client = PoliciesClient(self.auth_provider, **params_v3)
- self.projects_client = ProjectsClient(self.auth_provider, **params_v3)
- self.regions_client = RegionsClient(self.auth_provider, **params_v3)
- self.credentials_client = CredentialsClient(self.auth_provider,
- **params_v3)
- self.groups_client = GroupsClient(self.auth_provider, **params_v3)
+ self.trusts_client = identity.v3.TrustsClient(self.auth_provider,
+ **params_v3)
+ self.users_v3_client = identity.v3.UsersClient(self.auth_provider,
+ **params_v3)
+ self.endpoints_v3_client = identity.v3.EndPointsClient(
+ self.auth_provider, **params_v3)
+ self.roles_v3_client = identity.v3.RolesClient(self.auth_provider,
+ **params_v3)
+ self.identity_services_v3_client = identity.v3.ServicesClient(
+ self.auth_provider, **params_v3)
+ self.policies_client = identity.v3.PoliciesClient(self.auth_provider,
+ **params_v3)
+ self.projects_client = identity.v3.ProjectsClient(self.auth_provider,
+ **params_v3)
+ self.regions_client = identity.v3.RegionsClient(self.auth_provider,
+ **params_v3)
+ self.credentials_client = identity.v3.CredentialsClient(
+ self.auth_provider, **params_v3)
+ self.groups_client = identity.v3.GroupsClient(self.auth_provider,
+ **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
# API version is marked as enabled
if CONF.identity_feature_enabled.api_v2:
if CONF.identity.uri:
- self.token_client = TokenClient(
+ self.token_client = identity.v2.TokenClient(
CONF.identity.uri, **self.default_params)
else:
msg = 'Identity v2 API enabled, but no identity.uri set'
raise exceptions.InvalidConfiguration(msg)
if CONF.identity_feature_enabled.api_v3:
if CONF.identity.uri_v3:
- self.token_v3_client = V3TokenClient(
+ self.token_v3_client = identity.v3.V3TokenClient(
CONF.identity.uri_v3, **self.default_params)
else:
msg = 'Identity v3 API enabled, but no identity.uri_v3 set'
@@ -573,47 +350,50 @@
}
params.update(self.default_params)
- self.volume_qos_client = QosSpecsClient(self.auth_provider,
- **params)
- self.volume_qos_v2_client = QosSpecsV2Client(
+ self.volume_qos_client = volume.v1.QosSpecsClient(self.auth_provider,
+ **params)
+ self.volume_qos_v2_client = volume.v2.QosSpecsClient(
self.auth_provider, **params)
- self.volume_services_client = VolumeServicesClient(
+ self.volume_services_client = volume.v1.ServicesClient(
self.auth_provider, **params)
- self.volume_services_v2_client = VolumeServicesV2Client(
+ self.volume_services_v2_client = volume.v2.ServicesClient(
self.auth_provider, **params)
- self.backups_client = BackupsClient(self.auth_provider, **params)
- self.backups_v2_client = BackupsV2Client(self.auth_provider,
- **params)
- self.snapshots_client = SnapshotsClient(self.auth_provider,
- **params)
- self.snapshots_v2_client = SnapshotsV2Client(self.auth_provider,
- **params)
- self.volumes_client = VolumesClient(
+ self.backups_client = volume.v1.BackupsClient(self.auth_provider,
+ **params)
+ self.backups_v2_client = volume.v2.BackupsClient(self.auth_provider,
+ **params)
+ self.snapshots_client = volume.v1.SnapshotsClient(self.auth_provider,
+ **params)
+ self.snapshots_v2_client = volume.v2.SnapshotsClient(
+ self.auth_provider, **params)
+ self.volumes_client = volume.v1.VolumesClient(
self.auth_provider, default_volume_size=CONF.volume.volume_size,
**params)
- self.volumes_v2_client = VolumesV2Client(
+ self.volumes_v2_client = volume.v2.VolumesClient(
self.auth_provider, default_volume_size=CONF.volume.volume_size,
**params)
- self.volume_types_client = VolumeTypesClient(self.auth_provider,
- **params)
- self.volume_types_v2_client = VolumeTypesV2Client(
+ self.volume_messages_client = volume.v3.MessagesClient(
self.auth_provider, **params)
- self.volume_hosts_client = VolumeHostsClient(self.auth_provider,
- **params)
- self.volume_hosts_v2_client = VolumeHostsV2Client(
- self.auth_provider, **params)
- self.volume_quotas_client = VolumeQuotasClient(self.auth_provider,
- **params)
- self.volume_quotas_v2_client = VolumeQuotasV2Client(self.auth_provider,
+ self.volume_types_client = volume.v1.TypesClient(self.auth_provider,
+ **params)
+ self.volume_types_v2_client = volume.v2.TypesClient(self.auth_provider,
**params)
- self.volumes_extension_client = VolumeExtensionsClient(
+ self.volume_hosts_client = volume.v1.HostsClient(self.auth_provider,
+ **params)
+ self.volume_hosts_v2_client = volume.v2.HostsClient(self.auth_provider,
+ **params)
+ self.volume_quotas_client = volume.v1.QuotasClient(self.auth_provider,
+ **params)
+ self.volume_quotas_v2_client = volume.v2.QuotasClient(
self.auth_provider, **params)
- self.volumes_v2_extension_client = VolumeExtensionsV2Client(
+ self.volumes_extension_client = volume.v1.ExtensionsClient(
+ self.auth_provider, **params)
+ self.volumes_v2_extension_client = volume.v2.ExtensionsClient(
self.auth_provider, **params)
self.volume_availability_zone_client = \
- VolumeAvailabilityZoneClient(self.auth_provider, **params)
+ volume.v1.AvailabilityZoneClient(self.auth_provider, **params)
self.volume_v2_availability_zone_client = \
- VolumeAvailabilityZoneV2Client(self.auth_provider, **params)
+ volume.v2.AvailabilityZoneClient(self.auth_provider, **params)
def _set_object_storage_clients(self):
params = {
@@ -623,6 +403,9 @@
}
params.update(self.default_params_with_timeout_values)
- self.account_client = AccountClient(self.auth_provider, **params)
- self.container_client = ContainerClient(self.auth_provider, **params)
- self.object_client = ObjectClient(self.auth_provider, **params)
+ self.account_client = object_storage.AccountClient(self.auth_provider,
+ **params)
+ self.container_client = object_storage.ContainerClient(
+ self.auth_provider, **params)
+ self.object_client = object_storage.ObjectClient(self.auth_provider,
+ **params)
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 249261b..f9d7a9b 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -38,17 +38,17 @@
of your cloud to operate properly. The corresponding info can be given either
through CLI options or environment variables.
-You're probably familiar with these, but just to remind::
+You're probably familiar with these, but just to remind:
- +----------+---------------------------+----------------------+
- | Param | CLI | Environment Variable |
- +----------+---------------------------+----------------------+
- | Username | --os-username | OS_USERNAME |
- | Password | --os-password | OS_PASSWORD |
- | Project | --os-project-name | OS_PROJECT_NAME |
- | Tenant | --os-tenant-name (depr.) | OS_TENANT_NAME |
- | Domain | --os-domain-name | OS_DOMAIN_NAME |
- +----------+---------------------------+----------------------+
+======== ======================== ====================
+Param CLI Environment Variable
+======== ======================== ====================
+Username --os-username OS_USERNAME
+Password --os-password OS_PASSWORD
+Project --os-project-name OS_PROJECT_NAME
+Tenant --os-tenant-name (depr.) OS_TENANT_NAME
+Domain --os-domain-name OS_DOMAIN_NAME
+======== ======================== ====================
Optional Arguments
------------------
@@ -157,7 +157,8 @@
spec.append([CONF.object_storage.operator_role])
spec.append([CONF.object_storage.reseller_admin_role])
if CONF.service_available.heat:
- spec.append([CONF.orchestration.stack_owner_role])
+ spec.append([CONF.orchestration.stack_owner_role,
+ CONF.object_storage.operator_role])
if admin:
spec.append('admin')
resources = []
@@ -234,7 +235,7 @@
parser.add_argument('-r', '--concurrency',
default=1,
type=int,
- required=True,
+ required=False,
dest='concurrency',
help='Concurrency count')
parser.add_argument('--with-admin',
@@ -277,12 +278,11 @@
def take_action(self, parsed_args):
try:
- return main(parsed_args)
+ main(parsed_args)
except Exception:
LOG.exception("Failure generating test accounts.")
traceback.print_exc()
raise
- return 0
def get_description(self):
return DESCRIPTION
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index caba4b5..80de6f5 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -71,9 +71,6 @@
class TempestCleanup(command.Command):
- def __init__(self, app, cmd):
- super(TempestCleanup, self).__init__(app, cmd)
-
def take_action(self, parsed_args):
try:
self.init(parsed_args)
@@ -83,7 +80,6 @@
LOG.exception("Failure during cleanup")
traceback.print_exc()
raise
- return 0
def init(self, parsed_args):
cleanup_service.init_conf()
@@ -233,15 +229,16 @@
def _add_admin(self, tenant_id):
rl_cl = self.admin_mgr.roles_client
needs_role = True
- roles = rl_cl.list_user_roles(tenant_id, self.admin_id)['roles']
+ roles = rl_cl.list_user_roles_on_project(tenant_id,
+ self.admin_id)['roles']
for role in roles:
if role['id'] == self.admin_role_id:
needs_role = False
LOG.debug("User already had admin privilege for this tenant")
if needs_role:
LOG.debug("Adding admin privilege for : %s" % tenant_id)
- rl_cl.assign_user_role(tenant_id, self.admin_id,
- self.admin_role_id)
+ rl_cl.create_user_role_on_project(tenant_id, self.admin_id,
+ self.admin_role_id)
self.admin_role_added.append(tenant_id)
def _remove_admin_role(self, tenant_id):
@@ -251,8 +248,9 @@
id_cl = credentials.AdminManager().identity_client
if (self._tenant_exists(tenant_id)):
try:
- id_cl.delete_user_role(tenant_id, self.admin_id,
- self.admin_role_id)
+ id_cl.delete_role_from_user_on_project(tenant_id,
+ self.admin_id,
+ self.admin_role_id)
except Exception as ex:
LOG.exception("Failed removing role from tenant which still"
"exists, exception: %s" % ex)
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 9e86b48..8d2cfdc 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -661,7 +661,7 @@
if self.is_preserve:
secgroups = self._filter_by_conf_networks(secgroups)
- LOG.debug("List count, %s securtiy_groups" % len(secgroups))
+ LOG.debug("List count, %s security_groups" % len(secgroups))
return secgroups
def delete(self):
diff --git a/etc/config-generator.tempest.conf b/tempest/cmd/config-generator.tempest.conf
similarity index 100%
rename from etc/config-generator.tempest.conf
rename to tempest/cmd/config-generator.tempest.conf
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index 77d62d3..e3788ab 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -18,6 +18,7 @@
import sys
from cliff import command
+from oslo_config import generator
from oslo_log import log as logging
from six import moves
@@ -39,45 +40,28 @@
def get_tempest_default_config_dir():
"""Get default config directory of tempest
- Returns the correct default config dir to support both cases of
- tempest being or not installed in a virtualenv.
- Cases considered:
- - no virtual env, python2: real_prefix and base_prefix not set
- - no virtual env, python3: real_prefix not set, base_prefix set and
- identical to prefix
- - virtualenv, python2: real_prefix and prefix are set and different
- - virtualenv, python3: real_prefix not set, base_prefix and prefix are
- set and identical
- - pyvenv, any python version: real_prefix not set, base_prefix and prefix
- are set and different
+ There are 3 dirs that get tried in priority order. First is /etc/tempest,
+ if that doesn't exist it looks for a tempest dir in the XDG_CONFIG_HOME
+ dir (defaulting to ~/.config/tempest) and last it tries for a
+ ~/.tempest/etc directory. If none of these exist a ~/.tempest/etc
+ directory will be created.
:return: default config dir
"""
- real_prefix = getattr(sys, 'real_prefix', None)
- base_prefix = getattr(sys, 'base_prefix', None)
- prefix = sys.prefix
global_conf_dir = '/etc/tempest'
- if (real_prefix is None and
- (base_prefix is None or base_prefix == prefix) and
- os.path.isdir(global_conf_dir)):
- # Probably not running in a virtual environment.
- # NOTE(andreaf) we cannot distinguish this case from the case of
- # a virtual environment created with virtualenv, and running python3.
- # Also if it appears we are not in virtual env and fail to find
- # global config: '/etc/tempest', fall back to
- # '[sys.prefix]/etc/tempest'
+ xdg_config = os.environ.get('XDG_CONFIG_HOME',
+ os.path.expanduser('~/.config'))
+ user_xdg_global_path = os.path.join(xdg_config, 'tempest')
+ user_global_path = os.path.join(os.path.expanduser('~'), '.tempest/etc')
+ if os.path.isdir(global_conf_dir):
return global_conf_dir
+ elif os.path.isdir(user_xdg_global_path):
+ return user_xdg_global_path
+ elif os.path.isdir(user_global_path):
+ return user_global_path
else:
- conf_dir = os.path.join(prefix, 'etc/tempest')
- if os.path.isdir(conf_dir):
- return conf_dir
- else:
- # NOTE: The prefix is gotten from the path which pyconfig.h is
- # installed under. Some envs contain it under /usr/include, not
- # /user/local/include. Then prefix becomes /usr on such envs.
- # However, etc/tempest is installed under /usr/local and the bove
- # path logic mismatches. This is a workaround for such envs.
- return os.path.join(prefix, 'local/etc/tempest')
+ os.makedirs(user_global_path)
+ return user_global_path
class TempestInit(command.Command):
@@ -125,19 +109,20 @@
else:
LOG.warning("Global config dir %s can't be found" % config_dir)
- def generate_sample_config(self, local_dir, config_dir):
- if os.path.isdir(config_dir):
- conf_generator = os.path.join(config_dir,
- 'config-generator.tempest.conf')
-
- subprocess.call(['oslo-config-generator', '--config-file',
- conf_generator],
- cwd=local_dir)
+ def generate_sample_config(self, local_dir):
+ conf_generator = os.path.join(os.path.dirname(__file__),
+ 'config-generator.tempest.conf')
+ output_file = os.path.join(local_dir, 'etc/tempest.conf.sample')
+ if os.path.isfile(conf_generator):
+ generator.main(['--config-file', conf_generator, '--output-file',
+ output_file])
else:
LOG.warning("Skipping sample config generation because global "
- "config dir %s can't be found" % config_dir)
+ "config file %s can't be found" % conf_generator)
def create_working_dir(self, local_dir, config_dir):
+ # make sure we are working with abspath however tempest init is called
+ local_dir = os.path.abspath(local_dir)
# Create local dir if missing
if not os.path.isdir(local_dir):
LOG.debug('Creating local working dir: %s' % local_dir)
@@ -162,7 +147,7 @@
# Create and copy local etc dir
self.copy_config(etc_dir, config_dir)
# Generate the sample config file
- self.generate_sample_config(local_dir, config_dir)
+ self.generate_sample_config(local_dir)
# Update local confs to reflect local paths
self.update_local_conf(config_path, lock_dir, log_dir)
# Generate a testr conf file
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 6a65fcb..08ad94f 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -126,6 +126,7 @@
from tempest.lib.services.compute import security_group_rules_client
from tempest.lib.services.compute import security_groups_client
from tempest.lib.services.compute import servers_client
+from tempest.lib.services.image.v2 import images_client
from tempest.lib.services.network import networks_client
from tempest.lib.services.network import ports_client
from tempest.lib.services.network import routers_client
@@ -134,7 +135,6 @@
from tempest.services.identity.v2.json import roles_client
from tempest.services.identity.v2.json import tenants_client
from tempest.services.identity.v2.json import users_client
-from tempest.services.image.v2.json import images_client
from tempest.services.object_storage import container_client
from tempest.services.object_storage import object_client
from tempest.services.volume.v1.json import volumes_client
@@ -319,7 +319,7 @@
existing = [x['name'] for x in body]
for tenant in tenants:
if tenant not in existing:
- admin.tenants.create_tenant(tenant)['tenant']
+ admin.tenants.create_tenant(name=tenant)['tenant']
else:
LOG.warning("Tenant '%s' already exists in this environment"
% tenant)
@@ -361,7 +361,7 @@
role = next(r for r in roles if r['name'] == swift_role)
LOG.debug(USERS[user])
try:
- admin.roles.assign_user_role(
+ admin.roles.create_user_role_on_project(
USERS[user]['tenant_id'],
USERS[user]['id'],
role['id'])
@@ -391,8 +391,9 @@
% u['name'])
except lib_exc.NotFound:
admin.users.create_user(
- u['name'], u['pass'], tenant['id'],
- "%s@%s" % (u['name'], tenant['id']),
+ name=u['name'], password=u['pass'],
+ tenantId=tenant['id'],
+ email="%s@%s" % (u['name'], tenant['id']),
enabled=True)
diff --git a/tempest/cmd/list_plugins.py b/tempest/cmd/list_plugins.py
index 1f1ff1a..5f6b3e6 100644
--- a/tempest/cmd/list_plugins.py
+++ b/tempest/cmd/list_plugins.py
@@ -30,7 +30,6 @@
class TempestListPlugins(command.Command):
def take_action(self, parsed_args):
self._list_plugins()
- return 0
def get_description(self):
return 'List all tempest plugins'
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
new file mode 100644
index 0000000..26bd418
--- /dev/null
+++ b/tempest/cmd/run.py
@@ -0,0 +1,182 @@
+# 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.
+
+"""
+Runs tempest tests
+
+This command is used for running the tempest tests
+
+Test Selection
+==============
+Tempest run has several options:
+
+ * **--regex/-r**: This is a selection regex like what testr uses. It will run
+ any tests that match on re.match() with the regex
+ * **--smoke**: Run all the tests tagged as smoke
+
+You can also use the **--list-tests** option in conjunction with selection
+arguments to list which tests will be run.
+
+Test Execution
+==============
+There are several options to control how the tests are executed. By default
+tempest will run in parallel with a worker for each CPU present on the machine.
+If you want to adjust the number of workers use the **--concurrency** option
+and if you want to run tests serially use **--serial**
+
+Test Output
+===========
+By default tempest run's output to STDOUT will be generated using the
+subunit-trace output filter. But, if you would prefer a subunit v2 stream be
+output to STDOUT use the **--subunit** flag
+
+"""
+
+import io
+import os
+import sys
+import threading
+
+from cliff import command
+from os_testr import subunit_trace
+from oslo_log import log as logging
+from testrepository.commands import run_argv
+
+from tempest import config
+
+
+LOG = logging.getLogger(__name__)
+CONF = config.CONF
+
+
+class TempestRun(command.Command):
+
+ def _set_env(self):
+ # NOTE(mtreinish): This is needed so that testr doesn't gobble up any
+ # stacktraces on failure.
+ if 'TESTR_PDB' in os.environ:
+ return
+ else:
+ os.environ["TESTR_PDB"] = ""
+
+ def take_action(self, parsed_args):
+ self._set_env()
+ returncode = 0
+ # Local execution mode
+ if os.path.isfile('.testr.conf'):
+ # If you're running in local execution mode and there is not a
+ # testrepository dir create one
+ if not os.path.isdir('.testrepository'):
+ returncode = run_argv(['testr', 'init'], sys.stdin, sys.stdout,
+ sys.stderr)
+ if returncode:
+ sys.exit(returncode)
+ else:
+ print("No .testr.conf file was found for local execution")
+ sys.exit(2)
+
+ regex = self._build_regex(parsed_args)
+ if parsed_args.list_tests:
+ argv = ['tempest', 'list-tests', regex]
+ returncode = run_argv(argv, sys.stdin, sys.stdout, sys.stderr)
+ else:
+ options = self._build_options(parsed_args)
+ returncode = self._run(regex, options)
+ sys.exit(returncode)
+
+ def get_description(self):
+ return 'Run tempest'
+
+ def get_parser(self, prog_name):
+ parser = super(TempestRun, self).get_parser(prog_name)
+ parser = self._add_args(parser)
+ return parser
+
+ def _add_args(self, parser):
+ # test selection args
+ regex = parser.add_mutually_exclusive_group()
+ regex.add_argument('--smoke', action='store_true',
+ help="Run the smoke tests only")
+ regex.add_argument('--regex', '-r', default='',
+ help='A normal testr selection regex used to '
+ 'specify a subset of tests to run')
+ # list only args
+ parser.add_argument('--list-tests', '-l', action='store_true',
+ help='List tests',
+ default=False)
+ # exectution args
+ parser.add_argument('--concurrency', '-w',
+ help="The number of workers to use, defaults to "
+ "the number of cpus")
+ parallel = parser.add_mutually_exclusive_group()
+ parallel.add_argument('--parallel', dest='parallel',
+ action='store_true',
+ help='Run tests in parallel (this is the'
+ ' default)')
+ parallel.add_argument('--serial', dest='parallel',
+ action='store_false',
+ help='Run tests serially')
+ # output args
+ parser.add_argument("--subunit", action='store_true',
+ help='Enable subunit v2 output')
+
+ parser.set_defaults(parallel=True)
+ return parser
+
+ def _build_regex(self, parsed_args):
+ regex = ''
+ if parsed_args.smoke:
+ regex = 'smoke'
+ elif parsed_args.regex:
+ regex = parsed_args.regex
+ return regex
+
+ def _build_options(self, parsed_args):
+ options = []
+ if parsed_args.subunit:
+ options.append("--subunit")
+ if parsed_args.parallel:
+ options.append("--parallel")
+ if parsed_args.concurrency:
+ options.append("--concurrency=%s" % parsed_args.concurrency)
+ return options
+
+ def _run(self, regex, options):
+ returncode = 0
+ argv = ['tempest', 'run', regex] + options
+ if '--subunit' in options:
+ returncode = run_argv(argv, sys.stdin, sys.stdout, sys.stderr)
+ else:
+ argv.append('--subunit')
+ stdin = io.StringIO()
+ stdout_r, stdout_w = os.pipe()
+ subunit_w = os.fdopen(stdout_w, 'wt')
+ subunit_r = os.fdopen(stdout_r)
+ returncodes = {}
+
+ def run_argv_thread():
+ returncodes['testr'] = run_argv(argv, stdin, subunit_w,
+ sys.stderr)
+ subunit_w.close()
+
+ run_thread = threading.Thread(target=run_argv_thread)
+ run_thread.start()
+ returncodes['subunit-trace'] = subunit_trace.trace(subunit_r,
+ sys.stdout)
+ run_thread.join()
+ subunit_r.close()
+ # python version of pipefail
+ if returncodes['testr']:
+ returncode = returncodes['testr']
+ elif returncodes['subunit-trace']:
+ returncode = returncodes['subunit-trace']
+ return returncode
diff --git a/tempest/cmd/run_stress.py b/tempest/cmd/run_stress.py
index 9c8552f..7502c23 100755
--- a/tempest/cmd/run_stress.py
+++ b/tempest/cmd/run_stress.py
@@ -97,7 +97,6 @@
LOG.exception("Failure in the stress test framework")
traceback.print_exc()
raise
- return 0
def get_description(self):
return 'Run tempest stress tests'
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 72ea894..4b12ecb 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -384,7 +384,7 @@
icreds = credentials.get_credentials_provider(
'verify_tempest_config', network_resources=net_resources)
try:
- os = clients.Manager(icreds.get_primary_creds())
+ os = clients.Manager(icreds.get_primary_creds().credentials)
services = check_service_availability(os, update)
results = {}
for service in ['nova', 'cinder', 'neutron', 'swift']:
@@ -418,12 +418,11 @@
def take_action(self, parsed_args):
try:
- return main(parsed_args)
+ main(parsed_args)
except Exception:
LOG.exception("Failure verifying configuration.")
traceback.print_exc()
raise
- return 0
if __name__ == "__main__":
main()
diff --git a/tempest/common/cred_client.py b/tempest/common/cred_client.py
index aac036b..2ca9f40 100644
--- a/tempest/common/cred_client.py
+++ b/tempest/common/cred_client.py
@@ -40,8 +40,9 @@
self.roles_client = roles_client
def create_user(self, username, password, project, email):
- user = self.users_client.create_user(
- username, password, project['id'], email)
+ params = self._create_user_params(username, password,
+ project['id'], email)
+ user = self.users_client.create_user(**params)
if 'user' in user:
user = user['user']
return user
@@ -101,6 +102,13 @@
users_client,
roles_client)
+ def _create_user_params(self, username, password, project_id, email):
+ params = {'name': username,
+ 'password': password,
+ 'tenantId': project_id,
+ 'email': email}
+ return params
+
def create_project(self, name, description):
tenant = self.projects_client.create_tenant(
name=name, description=description)['tenant']
@@ -121,8 +129,9 @@
password=password)
def _assign_user_role(self, project, user, role):
- self.roles_client.assign_user_role(project['id'], user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(project['id'],
+ user['id'],
+ role['id'])
class V3CredsClient(CredsClient):
@@ -143,6 +152,13 @@
msg = "Requested domain %s could not be found" % domain_name
raise lib_exc.InvalidCredentials(msg)
+ def _create_user_params(self, username, password, project_id, email):
+ params = {'user_name': username,
+ 'password': password,
+ 'project_id': project_id,
+ 'email': email}
+ return params
+
def create_project(self, name, description):
project = self.projects_client.create_project(
name=name, description=description,
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index 5cce0bb..bf6c537 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -16,8 +16,8 @@
import six
-from tempest import exceptions
from tempest.lib import auth
+from tempest.lib import exceptions
@six.add_metaclass(abc.ABCMeta)
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 4873fcf..c22afc1 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -193,17 +193,21 @@
# Wrapper around auth.get_credentials to use the configured identity version
-# is none is specified
+# if none is specified
def get_credentials(fill_in=True, identity_version=None, **kwargs):
params = dict(DEFAULT_PARAMS, **kwargs)
identity_version = identity_version or CONF.identity.auth_version
# In case of "v3" add the domain from config if not specified
+ # To honour the "default_credentials_domain_name", if not domain
+ # field is specified at all, add it the credential dict.
if identity_version == 'v3':
domain_fields = set(x for x in auth.KeystoneV3Credentials.ATTRIBUTES
if 'domain' in x)
if not domain_fields.intersection(kwargs.keys()):
domain_name = CONF.auth.default_credentials_domain_name
- params['user_domain_name'] = domain_name
+ # NOTE(andreaf) Setting domain_name implicitly sets user and
+ # project domain names, if they are None
+ params['domain_name'] = domain_name
auth_url = CONF.identity.uri_v3
else:
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 3a3e3c2..b0c01f5 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -96,28 +96,35 @@
os.networks_client, os.routers_client, os.subnets_client,
os.ports_client, os.security_groups_client)
else:
- return (os.identity_v3_client, os.projects_client,
- os.users_v3_client, os.roles_v3_client, os.domains_client,
+ # We use a dedicated client manager for identity client in case we
+ # need a different token scope for them.
+ scope = 'domain' if CONF.identity.admin_domain_scope else 'project'
+ identity_os = clients.Manager(self.default_admin_creds,
+ scope=scope)
+ return (identity_os.identity_v3_client,
+ identity_os.projects_client,
+ identity_os.users_v3_client, identity_os.roles_v3_client,
+ identity_os.domains_client,
os.networks_client, os.routers_client,
os.subnets_client, os.ports_client,
os.security_groups_client)
- def _create_creds(self, suffix="", admin=False, roles=None):
- """Create random credentials under the following schema.
+ def _create_creds(self, admin=False, roles=None):
+ """Create credentials with random name.
- If the name contains a '.' is the full class path of something, and
- we don't really care. If it isn't, it's probably a meaningful name,
- so use it.
+ Creates project and user. When admin flag is True create user
+ with admin role. Assign user with additional roles (for example
+ _member_) and roles requested by caller.
- For logging purposes, -user and -tenant are long and redundant,
- don't use them. The user# will be sufficient to figure it out.
+ :param admin: Flag if to assign to the user admin role
+ :type admin: bool
+ :param roles: Roles to assign for the user
+ :type roles: list
+ :return: Readonly Credentials with network resources
"""
- if '.' in self.name:
- root = ""
- else:
- root = self.name
+ root = self.name
- project_name = data_utils.rand_name(root) + suffix
+ project_name = data_utils.rand_name(root)
project_desc = project_name + "-desc"
project = self.creds_client.create_project(
name=project_name, description=project_desc)
@@ -126,17 +133,17 @@
# having the same ID in both makes it easier to match them and debug.
username = project_name
user_password = data_utils.rand_password()
- email = data_utils.rand_name(root) + suffix + "@example.com"
+ email = data_utils.rand_name(root) + "@example.com"
user = self.creds_client.create_user(
username, user_password, project, email)
if 'user' in user:
user = user['user']
role_assigned = False
if admin:
- self.creds_client.assign_user_role(user, project,
- self.admin_role)
+ self.creds_client.assign_user_role(user, project, self.admin_role)
role_assigned = True
- if self.identity_version == 'v3':
+ if (self.identity_version == 'v3' and
+ CONF.identity.admin_domain_scope):
self.creds_client.assign_user_role_on_domain(
user, CONF.identity.admin_role)
# Add roles specified in config file
diff --git a/tempest/common/image.py b/tempest/common/image.py
new file mode 100644
index 0000000..42ce5ac
--- /dev/null
+++ b/tempest/common/image.py
@@ -0,0 +1,38 @@
+# Copyright 2016 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.
+
+
+def get_image_meta_from_headers(resp):
+ meta = {'properties': {}}
+ for key in resp.response:
+ value = resp.response[key]
+ if key.startswith('x-image-meta-property-'):
+ _key = key[22:]
+ meta['properties'][_key] = value
+ elif key.startswith('x-image-meta-'):
+ _key = key[13:]
+ meta[_key] = value
+
+ for key in ['is_public', 'protected', 'deleted']:
+ if key in meta:
+ meta[key] = meta[key].strip().lower() in ('t', 'true', 'yes', '1')
+
+ for key in ['size', 'min_ram', 'min_disk']:
+ if key in meta:
+ try:
+ meta[key] = int(meta[key])
+ except ValueError:
+ pass
+ return meta
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 42c69d5..5992d24 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -86,10 +86,8 @@
self.test_accounts_file = test_accounts_file
if test_accounts_file:
accounts = read_accounts_yaml(self.test_accounts_file)
- self.use_default_creds = False
else:
- accounts = {}
- self.use_default_creds = True
+ raise lib_exc.InvalidCredentials("No accounts file specified")
self.hash_dict = self.get_hash_dict(
accounts, admin_role, object_storage_operator_role,
object_storage_reseller_admin_role)
@@ -165,12 +163,7 @@
return hash_dict
def is_multi_user(self):
- # Default credentials is not a valid option with locking Account
- if self.use_default_creds:
- raise lib_exc.InvalidCredentials(
- "Account file %s doesn't exist" % self.test_accounts_file)
- else:
- return len(self.hash_dict['creds']) > 1
+ return len(self.hash_dict['creds']) > 1
def is_multi_tenant(self):
return self.is_multi_user()
@@ -245,9 +238,6 @@
return temp_creds
def _get_creds(self, roles=None):
- if self.use_default_creds:
- raise lib_exc.InvalidCredentials(
- "Account file %s doesn't exist" % self.test_accounts_file)
useable_hashes = self._get_match_hash_list(roles)
if len(useable_hashes) == 0:
msg = 'No users configured for type/roles %s' % roles
@@ -329,12 +319,9 @@
return self.get_creds_by_roles([self.admin_role])
def is_role_available(self, role):
- if self.use_default_creds:
- return False
- else:
- if self.hash_dict['roles'].get(role):
- return True
- return False
+ if self.hash_dict['roles'].get(role):
+ return True
+ return False
def admin_available(self):
return self.is_role_available(self.admin_role)
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 0a3e8d3..7cb9ebe 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -99,10 +99,10 @@
"""
self.ssh_client.test_connection_auth()
- def hostname_equals_servername(self, expected_hostname):
+ def get_hostname(self):
# Get host name using command "hostname"
actual_hostname = self.exec_command("hostname").rstrip()
- return expected_hostname == actual_hostname
+ return actual_hostname
def get_ram_size_in_mb(self):
output = self.exec_command('free -m | grep Mem')
diff --git a/tempest/common/validation_resources.py b/tempest/common/validation_resources.py
index c3c9a41..a55ee32 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -22,6 +22,26 @@
LOG = logging.getLogger(__name__)
+def _create_neutron_sec_group_rules(os, sec_group):
+ sec_group_rules_client = os.security_group_rules_client
+ ethertype = 'IPv4'
+ if CONF.validation.ip_version_for_ssh == 6:
+ ethertype = 'IPv6'
+
+ sec_group_rules_client.create_security_group_rule(
+ security_group_id=sec_group['id'],
+ protocol='tcp',
+ ethertype=ethertype,
+ port_range_min=22,
+ port_range_max=22,
+ direction='ingress')
+ sec_group_rules_client.create_security_group_rule(
+ security_group_id=sec_group['id'],
+ protocol='icmp',
+ ethertype=ethertype,
+ direction='ingress')
+
+
def create_ssh_security_group(os, add_rule=False):
security_groups_client = os.compute_security_groups_client
security_group_rules_client = os.compute_security_group_rules_client
@@ -30,12 +50,15 @@
security_group = security_groups_client.create_security_group(
name=sg_name, description=sg_description)['security_group']
if add_rule:
- security_group_rules_client.create_security_group_rule(
- parent_group_id=security_group['id'], ip_protocol='tcp',
- from_port=22, to_port=22)
- security_group_rules_client.create_security_group_rule(
- parent_group_id=security_group['id'], ip_protocol='icmp',
- from_port=-1, to_port=-1)
+ if CONF.service_available.neutron:
+ _create_neutron_sec_group_rules(os, security_group)
+ else:
+ security_group_rules_client.create_security_group_rule(
+ parent_group_id=security_group['id'], ip_protocol='tcp',
+ from_port=22, to_port=22)
+ security_group_rules_client.create_security_group_rule(
+ parent_group_id=security_group['id'], ip_protocol='icmp',
+ from_port=-1, to_port=-1)
LOG.debug("SSH Validation resource security group with tcp and icmp "
"rules %s created"
% sg_name)
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 23d7f88..d8dad69 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -15,6 +15,7 @@
from oslo_log import log as logging
+from tempest.common import image as common_image
from tempest import config
from tempest import exceptions
from tempest.lib.common.utils import misc as misc_utils
@@ -127,7 +128,11 @@
# The 'check_image' method is used here because the show_image method
# returns image details plus the image itself which is very expensive.
# The 'check_image' method returns just image details.
- show_image = client.check_image
+ def _show_image_v1(image_id):
+ resp = client.check_image(image_id)
+ return common_image.get_image_meta_from_headers(resp)
+
+ show_image = _show_image_v1
else:
show_image = client.show_image
diff --git a/tempest/config.py b/tempest/config.py
index 3810ceb..b3d409f 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -166,6 +166,10 @@
cfg.StrOpt('default_domain_id',
default='default',
help="ID of the default domain"),
+ cfg.BoolOpt('admin_domain_scope',
+ default=False,
+ help="Whether keystone identity v3 policy required "
+ "a domain scoped token to use admin APIs")
]
identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
@@ -678,6 +682,24 @@
cfg.IntOpt('volume_size',
default=1,
help='Default size in GB for volumes created by volumes tests'),
+ cfg.StrOpt('min_microversion',
+ default=None,
+ help="Lower version of the test target microversion range. "
+ "The format is 'X.Y', where 'X' and 'Y' are int values. "
+ "Tempest selects tests based on the range between "
+ "min_microversion and max_microversion. "
+ "If both values are not specified, Tempest avoids tests "
+ "which require a microversion. Valid values are string "
+ "with format 'X.Y' or string 'latest'",),
+ cfg.StrOpt('max_microversion',
+ default=None,
+ help="Upper version of the test target microversion range. "
+ "The format is 'X.Y', where 'X' and 'Y' are int values. "
+ "Tempest selects tests based on the range between "
+ "min_microversion and max_microversion. "
+ "If both values are not specified, Tempest avoids tests "
+ "which require a microversion. Valid values are string "
+ "with format 'X.Y' or string 'latest'",),
]
volume_feature_group = cfg.OptGroup(name='volume-feature-enabled',
@@ -707,6 +729,9 @@
cfg.BoolOpt('api_v2',
default=True,
help="Is the v2 volume API enabled"),
+ cfg.BoolOpt('api_v3',
+ default=False,
+ help="Is the v3 volume API enabled"),
cfg.BoolOpt('bootable',
default=True,
help='Update bootable status of a volume '
@@ -1010,23 +1035,28 @@
input_scenario_group = cfg.OptGroup(name="input-scenario",
title="Filters and values for"
- " input scenarios")
+ " input scenarios[DEPRECATED]")
+
InputScenarioGroup = [
cfg.StrOpt('image_regex',
default='^cirros-0.3.1-x86_64-uec$',
- help="Matching images become parameters for scenario tests"),
+ help="Matching images become parameters for scenario tests",
+ deprecated_for_removal=True),
cfg.StrOpt('flavor_regex',
default='^m1.nano$',
- help="Matching flavors become parameters for scenario tests"),
+ help="Matching flavors become parameters for scenario tests",
+ deprecated_for_removal=True),
cfg.StrOpt('non_ssh_image_regex',
default='^.*[Ww]in.*$',
help="SSH verification in tests is skipped"
- "for matching images"),
+ "for matching images",
+ deprecated_for_removal=True),
cfg.StrOpt('ssh_user_regex',
default="[[\"^.*[Cc]irros.*$\", \"cirros\"]]",
help="List of user mapped to regex "
- "to matching image names."),
+ "to matching image names.",
+ deprecated_for_removal=True),
]
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 92f335f..f534f30 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -13,157 +13,65 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
+
+from tempest.lib import exceptions
-class TempestException(Exception):
- """Base Tempest Exception
-
- To correctly use this class, inherit from it and define
- a 'message' property. That message will get printf'd
- with the keyword arguments provided to the constructor.
- """
- message = "An unknown exception occurred"
-
- def __init__(self, *args, **kwargs):
- super(TempestException, self).__init__()
- try:
- self._error_string = self.message % kwargs
- except Exception:
- # at least get the core message out if something happened
- self._error_string = self.message
- if len(args) > 0:
- # 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
- # Convert all arguments into their string representations...
- args = ["%s" % arg for arg in args]
- self._error_string = (self._error_string +
- "\nDetails: %s" % '\n'.join(args))
-
- def __str__(self):
- return self._error_string
-
-
-class RestClientException(TempestException,
- testtools.TestCase.failureException):
- pass
-
-
-class InvalidConfiguration(TempestException):
+class InvalidConfiguration(exceptions.TempestException):
message = "Invalid Configuration"
-class InvalidCredentials(TempestException):
- message = "Invalid Credentials"
-
-
-class InvalidServiceTag(TempestException):
+class InvalidServiceTag(exceptions.TempestException):
message = "Invalid service tag"
-class InvalidIdentityVersion(TempestException):
- message = "Invalid version %(identity_version)s of the identity service"
-
-
-class TimeoutException(TempestException):
+class TimeoutException(exceptions.TempestException):
message = "Request timed out"
-class BuildErrorException(TempestException):
+class BuildErrorException(exceptions.TempestException):
message = "Server %(server_id)s failed to build and is in ERROR status"
-class ImageKilledException(TempestException):
+class ImageKilledException(exceptions.TempestException):
message = "Image %(image_id)s 'killed' while waiting for '%(status)s'"
-class AddImageException(TempestException):
+class AddImageException(exceptions.TempestException):
message = "Image %(image_id)s failed to become ACTIVE in the allotted time"
-class VolumeBuildErrorException(TempestException):
+class VolumeBuildErrorException(exceptions.TempestException):
message = "Volume %(volume_id)s failed to build and is in ERROR status"
-class VolumeRestoreErrorException(TempestException):
+class VolumeRestoreErrorException(exceptions.TempestException):
message = "Volume %(volume_id)s failed to restore and is in ERROR status"
-class SnapshotBuildErrorException(TempestException):
+class SnapshotBuildErrorException(exceptions.TempestException):
message = "Snapshot %(snapshot_id)s failed to build and is in ERROR status"
-class VolumeBackupException(TempestException):
+class VolumeBackupException(exceptions.TempestException):
message = "Volume backup %(backup_id)s failed and is in ERROR status"
-class StackBuildErrorException(TempestException):
+class StackBuildErrorException(exceptions.TempestException):
message = ("Stack %(stack_identifier)s is in %(stack_status)s status "
"due to '%(stack_status_reason)s'")
-class EndpointNotFound(TempestException):
- message = "Endpoint not found"
-
-
-class IdentityError(TempestException):
- message = "Got identity error"
-
-
-class ServerUnreachable(TempestException):
+class ServerUnreachable(exceptions.TempestException):
message = "The server is not reachable via the configured network"
# NOTE(andreaf) This exception is added here to facilitate the migration
# of get_network_from_name and preprov_creds to tempest.lib, and it should
# be migrated along with them
-class InvalidTestResource(TempestException):
+class InvalidTestResource(exceptions.TempestException):
message = "%(name) is not a valid %(type), or the name is ambiguous"
-class RFCViolation(RestClientException):
+class RFCViolation(exceptions.RestClientException):
message = "RFC Violation"
-
-
-class InvalidHttpSuccessCode(RestClientException):
- message = "The success code is different than the expected one"
-
-
-class BadRequest(RestClientException):
- message = "Bad request"
-
-
-class ResponseWithNonEmptyBody(RFCViolation):
- message = ("RFC Violation! Response with %(status)d HTTP Status Code "
- "MUST NOT have a body")
-
-
-class ResponseWithEntity(RFCViolation):
- message = ("RFC Violation! Response with 205 HTTP Status Code "
- "MUST NOT have an entity")
-
-
-class InvalidHTTPResponseHeader(RestClientException):
- message = "HTTP response header is invalid"
-
-
-class InvalidStructure(TempestException):
- message = "Invalid structure of table with details"
-
-
-class CommandFailed(Exception):
- def __init__(self, returncode, cmd, output, stderr):
- super(CommandFailed, self).__init__()
- self.returncode = returncode
- self.cmd = cmd
- self.stdout = output
- self.stderr = stderr
-
- def __str__(self):
- return ("Command '%s' returned non-zero exit status %d.\n"
- "stdout:\n%s\n"
- "stderr:\n%s" % (self.cmd,
- self.returncode,
- self.stdout,
- self.stderr))
diff --git a/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py b/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
index c6c4deb..15224c5 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
@@ -19,7 +19,23 @@
'type': 'array',
'items': {'type': 'string'}
},
- 'log': {'type': 'object'},
+ 'log': {
+ 'type': 'object',
+ 'patternProperties': {
+ # NOTE: Here is a host name.
+ '^.+$': {
+ 'type': 'object',
+ 'properties': {
+ 'state': {'type': 'string'},
+ 'instances': {'type': 'integer'},
+ 'errors': {'type': 'integer'},
+ 'message': {'type': 'string'}
+ },
+ 'additionalProperties': False,
+ 'required': ['state', 'instances', 'errors', 'message']
+ }
+ }
+ },
'num_hosts': {'type': 'integer'},
'num_hosts_done': {'type': 'integer'},
'num_hosts_not_run': {'type': 'integer'},
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 974ba82..425758f 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -1,4 +1,5 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
+# Copyright 2016 Rackspace Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -179,7 +180,7 @@
:param headers: HTTP headers of the request
:param body: HTTP body in case of POST / PUT
:param filters: select a base URL out of the catalog
- :returns a Tuple (url, headers, body)
+ :return: a Tuple (url, headers, body)
"""
orig_req = dict(url=url, headers=headers, body=body)
@@ -223,6 +224,7 @@
Configure auth provider to provide alt authentication data
on a part of the *next* auth_request. If credentials are None,
set invalid data.
+
:param request_part: request part to contain invalid auth: url,
headers, body
:param auth_data: alternative auth_data from which to get the
@@ -260,7 +262,7 @@
disable_ssl_certificate_validation=None,
ca_certs=None, trace_requests=None, scope='project'):
super(KeystoneAuthProvider, self).__init__(credentials, scope)
- self.dsvm = disable_ssl_certificate_validation
+ self.dscv = disable_ssl_certificate_validation
self.ca_certs = ca_certs
self.trace_requests = trace_requests
self.auth_url = auth_url
@@ -339,7 +341,7 @@
def _auth_client(self, auth_url):
return json_v2id.TokenClient(
- auth_url, disable_ssl_certificate_validation=self.dsvm,
+ auth_url, disable_ssl_certificate_validation=self.dscv,
ca_certs=self.ca_certs, trace_requests=self.trace_requests)
def _auth_params(self):
@@ -368,18 +370,27 @@
def base_url(self, filters, auth_data=None):
"""Base URL from catalog
+ :param filters: Used to filter results
+
Filters can be:
- - service: compute, image, etc
- - region: the service region
- - endpoint_type: adminURL, publicURL, internalURL
- - api_version: replace catalog version with this
- - skip_path: take just the base URL
+
+ - service: service type name such as compute, image, etc.
+ - region: service region name
+ - name: service name, only if service exists
+ - endpoint_type: type of endpoint such as
+ adminURL, publicURL, internalURL
+ - api_version: the version of api used to replace catalog version
+ - skip_path: skips the suffix path of the url and uses base URL
+
+ :rtype: string
+ :return: url with filters applied
"""
if auth_data is None:
auth_data = self.get_auth()
token, _auth_data = auth_data
service = filters.get('service')
region = filters.get('region')
+ name = filters.get('name')
endpoint_type = filters.get('endpoint_type', 'publicURL')
if service is None:
@@ -388,17 +399,19 @@
_base_url = None
for ep in _auth_data['serviceCatalog']:
if ep["type"] == service:
+ if name is not None and ep["name"] != name:
+ continue
for _ep in ep['endpoints']:
if region is not None and _ep['region'] == region:
_base_url = _ep.get(endpoint_type)
if not _base_url:
- # No region matching, use the first
+ # No region or name matching, use the first
_base_url = ep['endpoints'][0].get(endpoint_type)
break
if _base_url is None:
raise exceptions.EndpointNotFound(
- "service: %s, region: %s, endpoint_type: %s" %
- (service, region, endpoint_type))
+ "service: %s, region: %s, endpoint_type: %s, name: %s" %
+ (service, region, endpoint_type, name))
return apply_url_filters(_base_url, filters)
def is_expired(self, auth_data):
@@ -415,7 +428,7 @@
def _auth_client(self, auth_url):
return json_v3id.V3TokenClient(
- auth_url, disable_ssl_certificate_validation=self.dsvm,
+ auth_url, disable_ssl_certificate_validation=self.dscv,
ca_certs=self.ca_certs, trace_requests=self.trace_requests)
def _auth_params(self):
@@ -489,18 +502,27 @@
the auth_data. In such case, as long as the requested service is
'identity', we can use the original auth URL to build the base_url.
+ :param filters: Used to filter results
+
Filters can be:
- - service: compute, image, etc
- - region: the service region
- - endpoint_type: adminURL, publicURL, internalURL
- - api_version: replace catalog version with this
- - skip_path: take just the base URL
+
+ - service: service type name such as compute, image, etc.
+ - region: service region name
+ - name: service name, only if service exists
+ - endpoint_type: type of endpoint such as
+ adminURL, publicURL, internalURL
+ - api_version: the version of api used to replace catalog version
+ - skip_path: skips the suffix path of the url and uses base URL
+
+ :rtype: string
+ :return: url with filters applied
"""
if auth_data is None:
auth_data = self.get_auth()
token, _auth_data = auth_data
service = filters.get('service')
region = filters.get('region')
+ name = filters.get('name')
endpoint_type = filters.get('endpoint_type', 'public')
if service is None:
@@ -510,19 +532,38 @@
endpoint_type = endpoint_type.replace('URL', '')
_base_url = None
catalog = _auth_data.get('catalog', [])
+
# Select entries with matching service type
service_catalog = [ep for ep in catalog if ep['type'] == service]
if len(service_catalog) > 0:
- service_catalog = service_catalog[0]['endpoints']
+ if name is not None:
+ service_catalog = (
+ [ep for ep in service_catalog if ep['name'] == name])
+ if len(service_catalog) > 0:
+ 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':
# 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.
+ msg = ('Got an empty catalog. Scope: %s. '
+ 'Falling back to configured URL for %s: %s')
+ LOG.debug(msg, self.scope, service, self.auth_url)
return apply_url_filters(self.auth_url, filters)
else:
# No matching service
- raise exceptions.EndpointNotFound(service)
+ msg = ('No matching service found in the catalog.\n'
+ 'Scope: %s, Credentials: %s\n'
+ 'Auth data: %s\n'
+ 'Service: %s, Region: %s, endpoint_type: %s\n'
+ 'Catalog: %s')
+ raise exceptions.EndpointNotFound(msg % (
+ self.scope, self.credentials, _auth_data, service, region,
+ endpoint_type, catalog))
# Filter by endpoint type (interface)
filtered_catalog = [ep for ep in service_catalog if
ep['interface'] == endpoint_type]
@@ -533,7 +574,7 @@
filtered_catalog = [ep for ep in filtered_catalog if
ep['region'] == region]
if len(filtered_catalog) == 0:
- # No matching region, take the first endpoint
+ # 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.
_base_url = filtered_catalog[0].get('url', None)
@@ -590,9 +631,9 @@
creds = credential_class(**kwargs)
# Fill in the credentials fields that were not specified
if fill_in:
- dsvm = disable_ssl_certificate_validation
+ dscv = disable_ssl_certificate_validation
auth_provider = auth_provider_class(
- creds, auth_url, disable_ssl_certificate_validation=dsvm,
+ creds, auth_url, disable_ssl_certificate_validation=dscv,
ca_certs=ca_certs, trace_requests=trace_requests)
creds = auth_provider.fill_credentials()
return creds
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index bf107d2..1b0f53a 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -54,6 +54,8 @@
:param auth_provider: an auth provider object used to wrap requests in auth
:param str service: The service name to use for the catalog lookup
:param str region: The region to use for the catalog lookup
+ :param str name: The endpoint name to use for the catalog lookup; this
+ returns only if the service exists
:param str endpoint_type: The endpoint type to use for the catalog lookup
:param int build_interval: Time in seconds between to status checks in
wait loops
@@ -76,10 +78,11 @@
endpoint_type='publicURL',
build_interval=1, build_timeout=60,
disable_ssl_certificate_validation=False, ca_certs=None,
- trace_requests=''):
+ trace_requests='', name=None):
self.auth_provider = auth_provider
self.service = service
self.region = region
+ self.name = name
self.endpoint_type = endpoint_type
self.build_interval = build_interval
self.build_timeout = build_timeout
@@ -191,7 +194,8 @@
_filters = dict(
service=self.service,
endpoint_type=self.endpoint_type,
- region=self.region
+ region=self.region,
+ name=self.name
)
if self.api_version is not None:
_filters['api_version'] = self.api_version
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 45e5067..7fcec8c 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -19,6 +19,7 @@
import string
import uuid
+from oslo_utils import netutils
import six.moves
@@ -153,7 +154,7 @@
This generates a string with an arbitrary number of characters, generated
by looping the base_text string. If the size is smaller than the size of
base_text, returning string is shrinked to the size.
- :param int size: a returning charactors size
+ :param int size: a returning characters size
:param str base_text: a string you want to repeat
:return: size string
:rtype: string
@@ -183,7 +184,7 @@
:rtype: netaddr.IPAddress
"""
# Check if the prefix is IPv4 address
- is_ipv4 = netaddr.valid_ipv4(cidr)
+ is_ipv4 = netutils.is_valid_ipv4(cidr)
if is_ipv4:
msg = "Unable to generate IP address by EUI64 for IPv4 prefix"
raise TypeError(msg)
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index 259bbbb..2a6a788 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -149,6 +149,10 @@
message = "Unexpected response code received"
+class InvalidIdentityVersion(TempestException):
+ message = "Invalid version %(identity_version)s of the identity service"
+
+
class InvalidStructure(TempestException):
message = "Invalid structure of table with details"
diff --git a/tempest/lib/services/compute/__init__.py b/tempest/lib/services/compute/__init__.py
index e69de29..91e896a 100644
--- a/tempest/lib/services/compute/__init__.py
+++ b/tempest/lib/services/compute/__init__.py
@@ -0,0 +1,77 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.compute.agents_client import AgentsClient
+from tempest.lib.services.compute.aggregates_client import AggregatesClient
+from tempest.lib.services.compute.availability_zone_client import \
+ AvailabilityZoneClient
+from tempest.lib.services.compute.baremetal_nodes_client import \
+ BaremetalNodesClient
+from tempest.lib.services.compute.certificates_client import \
+ CertificatesClient
+from tempest.lib.services.compute.extensions_client import \
+ ExtensionsClient
+from tempest.lib.services.compute.fixed_ips_client import FixedIPsClient
+from tempest.lib.services.compute.flavors_client import FlavorsClient
+from tempest.lib.services.compute.floating_ip_pools_client import \
+ FloatingIPPoolsClient
+from tempest.lib.services.compute.floating_ips_bulk_client import \
+ FloatingIPsBulkClient
+from tempest.lib.services.compute.floating_ips_client import \
+ FloatingIPsClient
+from tempest.lib.services.compute.hosts_client import HostsClient
+from tempest.lib.services.compute.hypervisor_client import \
+ HypervisorClient
+from tempest.lib.services.compute.images_client import ImagesClient
+from tempest.lib.services.compute.instance_usage_audit_log_client import \
+ InstanceUsagesAuditLogClient
+from tempest.lib.services.compute.interfaces_client import InterfacesClient
+from tempest.lib.services.compute.keypairs_client import KeyPairsClient
+from tempest.lib.services.compute.limits_client import LimitsClient
+from tempest.lib.services.compute.migrations_client import MigrationsClient
+from tempest.lib.services.compute.networks_client import NetworksClient
+from tempest.lib.services.compute.quota_classes_client import \
+ QuotaClassesClient
+from tempest.lib.services.compute.quotas_client import QuotasClient
+from tempest.lib.services.compute.security_group_default_rules_client import \
+ SecurityGroupDefaultRulesClient
+from tempest.lib.services.compute.security_group_rules_client import \
+ SecurityGroupRulesClient
+from tempest.lib.services.compute.security_groups_client import \
+ SecurityGroupsClient
+from tempest.lib.services.compute.server_groups_client import \
+ ServerGroupsClient
+from tempest.lib.services.compute.servers_client import ServersClient
+from tempest.lib.services.compute.services_client import ServicesClient
+from tempest.lib.services.compute.snapshots_client import SnapshotsClient
+from tempest.lib.services.compute.tenant_networks_client import \
+ TenantNetworksClient
+from tempest.lib.services.compute.tenant_usages_client import \
+ TenantUsagesClient
+from tempest.lib.services.compute.versions_client import VersionsClient
+from tempest.lib.services.compute.volumes_client import \
+ VolumesClient
+
+__all__ = ['AgentsClient', 'AggregatesClient', 'AvailabilityZoneClient',
+ 'BaremetalNodesClient', 'CertificatesClient', 'ExtensionsClient',
+ 'FixedIPsClient', 'FlavorsClient', 'FloatingIPPoolsClient',
+ 'FloatingIPsBulkClient', 'FloatingIPsClient', 'HostsClient',
+ 'HypervisorClient', 'ImagesClient', 'InstanceUsagesAuditLogClient',
+ 'InterfacesClient', 'KeyPairsClient', 'LimitsClient',
+ 'MigrationsClient', 'NetworksClient', 'QuotaClassesClient',
+ 'QuotasClient', 'SecurityGroupDefaultRulesClient',
+ 'SecurityGroupRulesClient', 'SecurityGroupsClient',
+ 'ServerGroupsClient', 'ServersClient', 'ServicesClient',
+ 'SnapshotsClient', 'TenantNetworksClient', 'TenantUsagesClient',
+ 'VersionsClient', 'VolumesClient']
diff --git a/tempest/lib/services/compute/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
index 168126c..ae747d8 100644
--- a/tempest/lib/services/compute/aggregates_client.py
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -108,7 +108,11 @@
return rest_client.ResponseBody(resp, body)
def set_metadata(self, aggregate_id, **kwargs):
- """Replace the aggregate's existing metadata with new metadata."""
+ """Replace the aggregate's existing metadata with new metadata.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#addAggregateMetadata
+ """
post_body = json.dumps({'set_metadata': kwargs})
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
post_body)
diff --git a/tempest/lib/services/compute/base_compute_client.py b/tempest/lib/services/compute/base_compute_client.py
index 433c94c..a706a79 100644
--- a/tempest/lib/services/compute/base_compute_client.py
+++ b/tempest/lib/services/compute/base_compute_client.py
@@ -36,11 +36,6 @@
api_microversion_header_name = 'X-OpenStack-Nova-API-Version'
- def __init__(self, auth_provider, service, region,
- **kwargs):
- super(BaseComputeClient, self).__init__(
- auth_provider, service, region, **kwargs)
-
def get_headers(self):
headers = super(BaseComputeClient, self).get_headers()
if COMPUTE_MICROVERSION:
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index e377c84..0d80a82 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -52,7 +52,7 @@
"""Create a new flavor or instance type.
Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#create-flavors
+ api-ref-compute-v2.1.html#createFlavor
"""
if kwargs.get('ephemeral'):
kwargs['OS-FLV-EXT-DATA:ephemeral'] = kwargs.pop('ephemeral')
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 8e4eca1..0d31ac7 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -315,7 +315,11 @@
return self.action(server_id, 'os-start', **kwargs)
def attach_volume(self, server_id, **kwargs):
- """Attaches a volume to a server instance."""
+ """Attaches a volume to a server instance.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#attachVolume
+ """
post_body = json.dumps({'volumeAttachment': kwargs})
resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
post_body)
diff --git a/tempest/services/identity/v2/json/endpoints_client.py b/tempest/lib/services/identity/v2/endpoints_client.py
similarity index 77%
rename from tempest/services/identity/v2/json/endpoints_client.py
rename to tempest/lib/services/identity/v2/endpoints_client.py
index ba9f867..f7b265d 100644
--- a/tempest/services/identity/v2/json/endpoints_client.py
+++ b/tempest/lib/services/identity/v2/endpoints_client.py
@@ -20,16 +20,14 @@
class EndpointsClient(rest_client.RestClient):
api_version = "v2.0"
- def create_endpoint(self, service_id, region_id, **kwargs):
- """Create an endpoint for service."""
- post_body = {
- 'service_id': service_id,
- 'region': region_id,
- 'publicurl': kwargs.get('publicurl'),
- 'adminurl': kwargs.get('adminurl'),
- 'internalurl': kwargs.get('internalurl')
- }
- post_body = json.dumps({'endpoint': post_body})
+ def create_endpoint(self, **kwargs):
+ """Create an endpoint for service.
+
+ Available params: http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#createEndpoint
+ """
+
+ post_body = json.dumps({'endpoint': kwargs})
resp, body = self.post('/endpoints', post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
diff --git a/tempest/services/image/v2/__init__.py b/tempest/lib/services/image/__init__.py
similarity index 100%
copy from tempest/services/image/v2/__init__.py
copy to tempest/lib/services/image/__init__.py
diff --git a/tempest/services/image/v2/__init__.py b/tempest/lib/services/image/v1/__init__.py
similarity index 100%
copy from tempest/services/image/v2/__init__.py
copy to tempest/lib/services/image/v1/__init__.py
diff --git a/tempest/services/image/v1/json/members_client.py b/tempest/lib/services/image/v1/image_members_client.py
similarity index 92%
rename from tempest/services/image/v1/json/members_client.py
rename to tempest/lib/services/image/v1/image_members_client.py
index 95cee1c..e7fa0c9 100644
--- a/tempest/services/image/v1/json/members_client.py
+++ b/tempest/lib/services/image/v1/image_members_client.py
@@ -15,7 +15,7 @@
from tempest.lib.common import rest_client
-class MembersClient(rest_client.RestClient):
+class ImageMembersClient(rest_client.RestClient):
api_version = "v1"
def list_image_members(self, image_id):
@@ -39,7 +39,7 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def add_member(self, member_id, image_id, **kwargs):
+ def create_image_member(self, image_id, member_id, **kwargs):
"""Add a member to an image.
Available params: see http://developer.openstack.org/
@@ -51,7 +51,7 @@
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp)
- def delete_member(self, member_id, image_id):
+ def delete_image_member(self, image_id, member_id):
"""Removes a membership from the image.
Available params: see http://developer.openstack.org/
diff --git a/tempest/lib/services/image/v2/__init__.py b/tempest/lib/services/image/v2/__init__.py
new file mode 100644
index 0000000..32bad8b
--- /dev/null
+++ b/tempest/lib/services/image/v2/__init__.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.image_members_client import \
+ ImageMembersClient
+from tempest.lib.services.image.v2.images_client import ImagesClient
+from tempest.lib.services.image.v2.namespaces_client import NamespacesClient
+from tempest.lib.services.image.v2.resource_types_client import \
+ ResourceTypesClient
+from tempest.lib.services.image.v2.schemas_client import SchemasClient
+
+__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespacesClient',
+ 'ResourceTypesClient', 'SchemasClient']
diff --git a/tempest/services/image/v2/json/members_client.py b/tempest/lib/services/image/v2/image_members_client.py
similarity index 97%
rename from tempest/services/image/v2/json/members_client.py
rename to tempest/lib/services/image/v2/image_members_client.py
index 233a6ec..2ae7516 100644
--- a/tempest/services/image/v2/json/members_client.py
+++ b/tempest/lib/services/image/v2/image_members_client.py
@@ -15,7 +15,7 @@
from tempest.lib.common import rest_client
-class MembersClient(rest_client.RestClient):
+class ImageMembersClient(rest_client.RestClient):
api_version = "v2"
def list_image_members(self, image_id):
diff --git a/tempest/services/image/v2/json/images_client.py b/tempest/lib/services/image/v2/images_client.py
similarity index 94%
rename from tempest/services/image/v2/json/images_client.py
rename to tempest/lib/services/image/v2/images_client.py
index aae1cd0..71e7c6b 100644
--- a/tempest/services/image/v2/json/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -131,10 +131,3 @@
resp, _ = self.delete(url)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp)
-
- def list_resource_types(self):
- url = 'metadefs/resource_types'
- resp, body = self.get(url)
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/image/v2/json/namespaces_client.py b/tempest/lib/services/image/v2/namespaces_client.py
similarity index 100%
rename from tempest/services/image/v2/json/namespaces_client.py
rename to tempest/lib/services/image/v2/namespaces_client.py
diff --git a/tempest/lib/services/image/v2/resource_types_client.py b/tempest/lib/services/image/v2/resource_types_client.py
new file mode 100644
index 0000000..1349c63
--- /dev/null
+++ b/tempest/lib/services/image/v2/resource_types_client.py
@@ -0,0 +1,29 @@
+# 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.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class ResourceTypesClient(rest_client.RestClient):
+ api_version = "v2"
+
+ def list_resource_types(self):
+ url = 'metadefs/resource_types'
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/image/v2/json/schemas_client.py b/tempest/lib/services/image/v2/schemas_client.py
similarity index 100%
rename from tempest/services/image/v2/json/schemas_client.py
rename to tempest/lib/services/image/v2/schemas_client.py
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
index e69de29..c466f07 100644
--- a/tempest/lib/services/network/__init__.py
+++ b/tempest/lib/services/network/__init__.py
@@ -0,0 +1,38 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.network.agents_client import AgentsClient
+from tempest.lib.services.network.extensions_client import ExtensionsClient
+from tempest.lib.services.network.floating_ips_client import FloatingIPsClient
+from tempest.lib.services.network.metering_label_rules_client import \
+ MeteringLabelRulesClient
+from tempest.lib.services.network.metering_labels_client import \
+ MeteringLabelsClient
+from tempest.lib.services.network.networks_client import NetworksClient
+from tempest.lib.services.network.ports_client import PortsClient
+from tempest.lib.services.network.quotas_client import QuotasClient
+from tempest.lib.services.network.routers_client import RoutersClient
+from tempest.lib.services.network.security_group_rules_client import \
+ SecurityGroupRulesClient
+from tempest.lib.services.network.security_groups_client import \
+ SecurityGroupsClient
+from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
+from tempest.lib.services.network.subnets_client import SubnetsClient
+from tempest.lib.services.network.versions_client import NetworkVersionsClient
+
+__all__ = ['AgentsClient', 'ExtensionsClient', 'FloatingIPsClient',
+ 'MeteringLabelRulesClient', 'MeteringLabelsClient',
+ 'NetworksClient', 'PortsClient', 'QuotasClient', 'RoutersClient',
+ 'SecurityGroupRulesClient', 'SecurityGroupsClient',
+ 'SubnetpoolsClient', 'SubnetsClient', 'NetworkVersionsClient']
diff --git a/tempest/lib/services/network/versions_client.py b/tempest/lib/services/network/versions_client.py
new file mode 100644
index 0000000..0202927
--- /dev/null
+++ b/tempest/lib/services/network/versions_client.py
@@ -0,0 +1,45 @@
+# Copyright 2016 VMware, 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 time
+
+from oslo_serialization import jsonutils as json
+from six.moves import urllib
+
+from tempest.lib.services.network import base
+
+
+class NetworkVersionsClient(base.BaseNetworkClient):
+
+ def list_versions(self):
+ """Do a GET / to fetch available API version information."""
+
+ endpoint = self.base_url
+ url = urllib.parse.urlparse(endpoint)
+ version_url = '%s://%s/' % (url.scheme, url.netloc)
+
+ # Note: we do a raw_request here because we want to use
+ # an unversioned URL, not "v2/$project_id/".
+ # Since raw_request doesn't log anything, we do that too.
+ start = time.time()
+ self._log_request_start('GET', version_url)
+ response, body = self.raw_request(version_url, 'GET')
+ end = time.time()
+ self._log_request('GET', version_url, response,
+ secs=(end - start), resp_body=body)
+
+ self.response_checker('GET', response, body)
+ self.expected_success(200, response.status)
+ body = json.loads(body)
+ return body
diff --git a/tempest/manager.py b/tempest/manager.py
index cafa5b9..f2659a8 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -13,10 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import cred_provider
from tempest import config
-from tempest import exceptions
from tempest.lib import auth
+from tempest.lib import exceptions
CONF = config.CONF
@@ -34,7 +33,7 @@
Credentials to be used within the various client classes managed by the
Manager object must be defined.
- :param credentials: type Credentials or TestResources
+ :param credentials: An instance of `auth.Credentials`
:param scope: default scope for tokens produced by the auth provider
"""
self.credentials = credentials
@@ -42,15 +41,9 @@
if not self.credentials.is_valid():
raise exceptions.InvalidCredentials()
self.auth_version = CONF.identity.auth_version
- # Tenant isolation creates TestResources, but
- # PreProvisionedCredentialProvider and some tests create Credentials
- if isinstance(credentials, cred_provider.TestResources):
- creds = self.credentials.credentials
- else:
- creds = self.credentials
# Creates an auth provider for the credentials
- self.auth_provider = get_auth_provider(creds, pre_auth=True,
- scope=scope)
+ self.auth_provider = get_auth_provider(
+ self.credentials, pre_auth=True, scope=scope)
def get_auth_provider_class(credentials):
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 1dfa86d..69da1c9 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -19,9 +19,11 @@
import netaddr
from oslo_log import log
from oslo_serialization import jsonutils as json
+from oslo_utils import netutils
import six
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 import waiters
@@ -29,7 +31,6 @@
from tempest import exceptions
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
-from tempest.scenario import network_resources
import tempest.test
CONF = config.CONF
@@ -223,7 +224,7 @@
port = self._create_port(network_id=net_id,
client=clients.ports_client,
**create_port_body)
- ports.append({'port': port.id})
+ ports.append({'port': port['id']})
if ports:
kwargs['networks'] = ports
self.ports = ports
@@ -468,7 +469,8 @@
cleanup_args=[_image_client.delete_image, image_id])
if CONF.image_feature_enabled.api_v1:
# In glance v1 the additional properties are stored in the headers.
- snapshot_image = _image_client.check_image(image_id)
+ resp = _image_client.check_image(image_id)
+ snapshot_image = common_image.get_image_meta_from_headers(resp)
image_props = snapshot_image.get('properties', {})
else:
# In glance v2 the additional properties are flattened.
@@ -704,12 +706,12 @@
tenant_id = networks_client.tenant_id
name = data_utils.rand_name(namestart)
result = networks_client.create_network(name=name, tenant_id=tenant_id)
- network = network_resources.DeletableNetwork(
- networks_client=networks_client, routers_client=routers_client,
- **result['network'])
- self.assertEqual(network.name, name)
+ network = result['network']
+
+ self.assertEqual(network['name'], name)
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- network.delete)
+ self.networks_client.delete_network,
+ network['id'])
return network
def _list_networks(self, *args, **kwargs):
@@ -779,13 +781,13 @@
# blocks until an unallocated block is found.
for subnet_cidr in tenant_cidr.subnet(num_bits):
str_cidr = str(subnet_cidr)
- if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
+ if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
continue
subnet = dict(
name=data_utils.rand_name(namestart),
- network_id=network.id,
- tenant_id=network.tenant_id,
+ network_id=network['id'],
+ tenant_id=network['tenant_id'],
cidr=str_cidr,
ip_version=ip_version,
**kwargs
@@ -798,11 +800,13 @@
if not is_overlapping_cidr:
raise
self.assertIsNotNone(result, 'Unable to allocate tenant network')
- subnet = network_resources.DeletableSubnet(
- subnets_client=subnets_client,
- routers_client=routers_client, **result['subnet'])
- self.assertEqual(subnet.cidr, str_cidr)
- self.addCleanup(test_utils.call_and_ignore_notfound_exc, subnet.delete)
+
+ subnet = result['subnet']
+ self.assertEqual(subnet['cidr'], str_cidr)
+
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ subnets_client.delete_subnet, subnet['id'])
+
return subnet
def _create_port(self, network_id, client=None, namestart='port-quotatest',
@@ -815,9 +819,9 @@
network_id=network_id,
**kwargs)
self.assertIsNotNone(result, 'Unable to allocate port')
- port = network_resources.DeletablePort(ports_client=client,
- **result['port'])
- self.addCleanup(test_utils.call_and_ignore_notfound_exc, port.delete)
+ port = result['port']
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ client.delete_port, port['id'])
return port
def _get_server_port_id_and_ip4(self, server, ip_addr=None):
@@ -834,7 +838,7 @@
port_map = [(p["id"], fxip["ip_address"])
for p in ports
for fxip in p["fixed_ips"]
- if netaddr.valid_ipv4(fxip["ip_address"])
+ if netutils.is_valid_ipv4(fxip["ip_address"])
and p['status'] in p_status]
inactive = [p for p in ports if p['status'] != 'ACTIVE']
if inactive:
@@ -852,7 +856,7 @@
net = self._list_networks(name=network_name)
self.assertNotEqual(len(net), 0,
"Unable to get network by name: %s" % network_name)
- return network_resources.AttributeDict(net[0])
+ return net[0]
def create_floating_ip(self, thing, external_network_id=None,
port_id=None, client=None):
@@ -871,44 +875,51 @@
tenant_id=thing['tenant_id'],
fixed_ip_address=ip4
)
- floating_ip = network_resources.DeletableFloatingIp(
- client=client,
- **result['floatingip'])
+ floating_ip = result['floatingip']
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- floating_ip.delete)
+ self.floating_ips_client.delete_floatingip,
+ floating_ip['id'])
return floating_ip
def _associate_floating_ip(self, floating_ip, server):
port_id, _ = self._get_server_port_id_and_ip4(server)
- floating_ip.update(port_id=port_id)
- self.assertEqual(port_id, floating_ip.port_id)
+ kwargs = dict(port_id=port_id)
+ floating_ip = self.floating_ips_client.update_floatingip(
+ floating_ip['id'], **kwargs)['floatingip']
+ self.assertEqual(port_id, floating_ip['port_id'])
return floating_ip
def _disassociate_floating_ip(self, floating_ip):
- """:param floating_ip: type DeletableFloatingIp"""
- floating_ip.update(port_id=None)
- self.assertIsNone(floating_ip.port_id)
+ """:param floating_ip: floating_ips_client.create_floatingip"""
+ kwargs = dict(port_id=None)
+ floating_ip = self.floating_ips_client.update_floatingip(
+ floating_ip['id'], **kwargs)['floatingip']
+ self.assertIsNone(floating_ip['port_id'])
return floating_ip
def check_floating_ip_status(self, floating_ip, status):
"""Verifies floatingip reaches the given status
- :param floating_ip: network_resources.DeletableFloatingIp floating
- IP to check status
+ :param dict floating_ip: floating IP dict to check status
:param status: target status
:raises: AssertionError if status doesn't match
"""
+ floatingip_id = floating_ip['id']
+
def refresh():
- floating_ip.refresh()
- return status == floating_ip.status
+ result = (self.floating_ips_client.
+ show_floatingip(floatingip_id)['floatingip'])
+ return status == result['status']
tempest.test.call_until_true(refresh,
CONF.network.build_timeout,
CONF.network.build_interval)
- self.assertEqual(status, floating_ip.status,
+ 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,
+ .format(fp=floating_ip, cst=floating_ip['status'],
st=status))
LOG.info("FloatingIP: {fp} is at status: {st}"
.format(fp=floating_ip, st=status))
@@ -981,8 +992,8 @@
secgroup=secgroup,
security_groups_client=security_groups_client)
for rule in rules:
- self.assertEqual(tenant_id, rule.tenant_id)
- self.assertEqual(secgroup.id, rule.security_group_id)
+ self.assertEqual(tenant_id, rule['tenant_id'])
+ self.assertEqual(secgroup['id'], rule['security_group_id'])
return secgroup
def _create_empty_security_group(self, client=None, tenant_id=None,
@@ -994,7 +1005,7 @@
- IPv6 egress to any
:param tenant_id: secgroup will be created in this tenant
- :returns: DeletableSecurityGroup -- containing the secgroup created
+ :returns: the created security group
"""
if client is None:
client = self.security_groups_client
@@ -1006,15 +1017,14 @@
description=sg_desc)
sg_dict['tenant_id'] = tenant_id
result = client.create_security_group(**sg_dict)
- secgroup = network_resources.DeletableSecurityGroup(
- client=client, routers_client=self.routers_client,
- **result['security_group']
- )
- self.assertEqual(secgroup.name, sg_name)
- self.assertEqual(tenant_id, secgroup.tenant_id)
- self.assertEqual(secgroup.description, sg_desc)
+
+ secgroup = result['security_group']
+ self.assertEqual(secgroup['name'], sg_name)
+ self.assertEqual(tenant_id, secgroup['tenant_id'])
+ self.assertEqual(secgroup['description'], sg_desc)
+
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- secgroup.delete)
+ client.delete_security_group, secgroup['id'])
return secgroup
def _default_security_group(self, client=None, tenant_id=None):
@@ -1027,13 +1037,12 @@
if not tenant_id:
tenant_id = client.tenant_id
sgs = [
- sg for sg in client.list_security_groups().values()[0]
+ sg for sg in list(client.list_security_groups().values())[0]
if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
]
msg = "No default security group for tenant %s." % (tenant_id)
self.assertTrue(len(sgs) > 0, msg)
- return network_resources.DeletableSecurityGroup(client=client,
- **sgs[0])
+ return sgs[0]
def _create_security_group_rule(self, secgroup=None,
sec_group_rules_client=None,
@@ -1044,7 +1053,7 @@
Create a rule in a secgroup. if secgroup not defined will search for
default secgroup in tenant_id.
- :param secgroup: type DeletableSecurityGroup.
+ :param secgroup: the security group.
:param tenant_id: if secgroup not passed -- the tenant in which to
search for default secgroup
:param kwargs: a dictionary containing rule parameters:
@@ -1066,17 +1075,15 @@
secgroup = self._default_security_group(
client=security_groups_client, tenant_id=tenant_id)
- ruleset = dict(security_group_id=secgroup.id,
- tenant_id=secgroup.tenant_id)
+ ruleset = dict(security_group_id=secgroup['id'],
+ tenant_id=secgroup['tenant_id'])
ruleset.update(kwargs)
sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
- sg_rule = network_resources.DeletableSecurityGroupRule(
- client=sec_group_rules_client,
- **sg_rule['security_group_rule']
- )
- self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
- self.assertEqual(secgroup.id, sg_rule.security_group_id)
+ sg_rule = sg_rule['security_group_rule']
+
+ self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
+ self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
return sg_rule
@@ -1130,7 +1137,7 @@
if msg not in ex._error_string:
raise ex
else:
- self.assertEqual(r_direction, sg_rule.direction)
+ self.assertEqual(r_direction, sg_rule['direction'])
rules.append(sg_rule)
return rules
@@ -1152,10 +1159,11 @@
network_id = CONF.network.public_network_id
if router_id:
body = client.show_router(router_id)
- return network_resources.AttributeDict(**body['router'])
+ return body['router']
elif network_id:
router = self._create_router(client, tenant_id)
- router.set_gateway(network_id)
+ kwargs = {'external_gateway_info': dict(network_id=network_id)}
+ router = client.update_router(router['id'], **kwargs)['router']
return router
else:
raise Exception("Neither of 'public_router_id' or "
@@ -1171,15 +1179,18 @@
result = client.create_router(name=name,
admin_state_up=True,
tenant_id=tenant_id)
- router = network_resources.DeletableRouter(routers_client=client,
- **result['router'])
- self.assertEqual(router.name, name)
- self.addCleanup(test_utils.call_and_ignore_notfound_exc, router.delete)
+ router = result['router']
+ self.assertEqual(router['name'], name)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ client.delete_router,
+ router['id'])
return router
def _update_router_admin_state(self, router, admin_state_up):
- router.update(admin_state_up=admin_state_up)
- self.assertEqual(admin_state_up, router.admin_state_up)
+ kwargs = dict(admin_state_up=admin_state_up)
+ router = self.routers_client.update_router(
+ router['id'], **kwargs)['router']
+ self.assertEqual(admin_state_up, router['admin_state_up'])
def create_networks(self, networks_client=None,
routers_client=None, subnets_client=None,
@@ -1212,7 +1223,6 @@
tenant_id=tenant_id)
router = self._get_router(client=routers_client,
tenant_id=tenant_id)
-
subnet_kwargs = dict(network=network,
subnets_client=subnets_client,
routers_client=routers_client)
@@ -1220,7 +1230,17 @@
if dns_nameservers is not None:
subnet_kwargs['dns_nameservers'] = dns_nameservers
subnet = self._create_subnet(**subnet_kwargs)
- subnet.add_to_router(router.id)
+ if not routers_client:
+ routers_client = self.routers_client
+ router_id = router['id']
+ routers_client.add_router_interface(router_id,
+ subnet_id=subnet['id'])
+
+ # save a cleanup job to remove this association between
+ # router and subnet
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ routers_client.remove_router_interface, router_id,
+ subnet_id=subnet['id'])
return network, subnet, router
diff --git a/tempest/scenario/network_resources.py b/tempest/scenario/network_resources.py
deleted file mode 100644
index 667476f..0000000
--- a/tempest/scenario/network_resources.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# Copyright 2013 Hewlett-Packard Development Company, L.P.
-# 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 abc
-import time
-
-import six
-
-from tempest import exceptions
-from tempest.lib.common.utils import misc
-
-
-class AttributeDict(dict):
- """Provide attribute access (dict.key) to dictionary values."""
-
- def __getattr__(self, name):
- """Allow attribute access for all keys in the dict."""
- if name in self:
- return self[name]
- return super(AttributeDict, self).__getattribute__(name)
-
-
-@six.add_metaclass(abc.ABCMeta)
-class DeletableResource(AttributeDict):
- """Support deletion of neutron resources (networks, subnets)
-
- via a delete() method, as is supported by keystone and nova resources.
- """
-
- def __init__(self, *args, **kwargs):
- self.client = kwargs.pop('client', None)
- self.networks_client = kwargs.pop('networks_client', None)
- self.routers_client = kwargs.pop('routers_client', None)
- self.subnets_client = kwargs.pop('subnets_client', None)
- self.ports_client = kwargs.pop('ports_client', None)
- super(DeletableResource, self).__init__(*args, **kwargs)
-
- def __str__(self):
- return '<%s id="%s" name="%s">' % (self.__class__.__name__,
- self.id, self.name)
-
- @abc.abstractmethod
- def delete(self):
- return
-
- @abc.abstractmethod
- def refresh(self):
- return
-
- def __hash__(self):
- return hash(self.id)
-
- def wait_for_status(self, status):
- if not hasattr(self, 'status'):
- return
-
- def helper_get():
- self.refresh()
- return self
-
- return self.wait_for_resource_status(helper_get, status)
-
- def wait_for_resource_status(self, fetch, status):
- """Waits for a network resource to reach a status
-
- @param fetch: the callable to be used to query the resource status
- @type fetch: callable that takes no parameters and returns the resource
- @param status: the status that the resource has to reach
- @type status: String
- """
- interval = self.build_interval
- timeout = self.build_timeout
- start_time = time.time()
-
- while time.time() - start_time <= timeout:
- resource = fetch()
- if resource['status'] == status:
- return
- time.sleep(interval)
-
- # At this point, the wait has timed out
- message = 'Resource %s' % (str(resource))
- message += ' failed to reach status %s' % status
- message += ' (current: %s)' % resource['status']
- message += ' within the required time %s' % timeout
- caller = misc.find_test_caller()
- if caller:
- message = '(%s) %s' % (caller, message)
- raise exceptions.TimeoutException(message)
-
-
-class DeletableNetwork(DeletableResource):
-
- def delete(self):
- self.networks_client.delete_network(self.id)
-
-
-class DeletableSubnet(DeletableResource):
-
- def __init__(self, *args, **kwargs):
- super(DeletableSubnet, self).__init__(*args, **kwargs)
- self._router_ids = set()
-
- def update(self, *args, **kwargs):
- result = self.subnets_client.update_subnet(self.id,
- *args,
- **kwargs)
- return super(DeletableSubnet, self).update(**result['subnet'])
-
- def add_to_router(self, router_id):
- self._router_ids.add(router_id)
- self.routers_client.add_router_interface(router_id,
- subnet_id=self.id)
-
- def delete(self):
- for router_id in self._router_ids.copy():
- self.routers_client.remove_router_interface(router_id,
- subnet_id=self.id)
- self._router_ids.remove(router_id)
- self.subnets_client.delete_subnet(self.id)
-
-
-class DeletableRouter(DeletableResource):
-
- def set_gateway(self, network_id):
- return self.update(external_gateway_info=dict(network_id=network_id))
-
- def unset_gateway(self):
- return self.update(external_gateway_info=dict())
-
- def update(self, *args, **kwargs):
- result = self.routers_client.update_router(self.id,
- *args,
- **kwargs)
- return super(DeletableRouter, self).update(**result['router'])
-
- def delete(self):
- self.unset_gateway()
- self.routers_client.delete_router(self.id)
-
-
-class DeletableFloatingIp(DeletableResource):
-
- def refresh(self, *args, **kwargs):
- result = self.client.show_floatingip(self.id,
- *args,
- **kwargs)
- super(DeletableFloatingIp, self).update(**result['floatingip'])
-
- def update(self, *args, **kwargs):
- result = self.client.update_floatingip(self.id,
- *args,
- **kwargs)
- super(DeletableFloatingIp, self).update(**result['floatingip'])
-
- def __repr__(self):
- return '<%s addr="%s">' % (self.__class__.__name__,
- self.floating_ip_address)
-
- def __str__(self):
- return '<"FloatingIP" addr="%s" id="%s">' % (self.floating_ip_address,
- self.id)
-
- def delete(self):
- self.client.delete_floatingip(self.id)
-
-
-class DeletablePort(DeletableResource):
-
- def delete(self):
- self.ports_client.delete_port(self.id)
-
-
-class DeletableSecurityGroup(DeletableResource):
-
- def delete(self):
- self.client.delete_security_group(self.id)
-
-
-class DeletableSecurityGroupRule(DeletableResource):
-
- def __repr__(self):
- return '<%s id="%s">' % (self.__class__.__name__, self.id)
-
- def delete(self):
- self.client.delete_security_group_rule(self.id)
-
-
-class DeletablePool(DeletableResource):
-
- def delete(self):
- self.client.delete_pool(self.id)
-
-
-class DeletableMember(DeletableResource):
-
- def delete(self):
- self.client.delete_member(self.id)
-
-
-class DeletableVip(DeletableResource):
-
- def delete(self):
- self.client.delete_vip(self.id)
-
- def refresh(self):
- result = self.client.show_vip(self.id)
- super(DeletableVip, self).update(**result['vip'])
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 4c2d31b..e4b699e 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -52,15 +52,18 @@
def _setup_network_and_servers(self):
keypair = self.create_keypair()
- security_group = self._create_security_group()
+ security_groups = []
+ if test.is_extension_enabled('security-group', 'network'):
+ security_group = self._create_security_group()
+ security_groups = [{'name': security_group['name']}]
network, subnet, router = self.create_networks()
public_network_id = CONF.network.public_network_id
server_name = data_utils.rand_name('server-smoke')
server = self.create_server(
name=server_name,
- networks=[{'uuid': network.id}],
+ networks=[{'uuid': network['id']}],
key_name=keypair['name'],
- security_groups=[{'name': security_group['name']}],
+ security_groups=security_groups,
wait_until='ACTIVE')
floating_ip = self.create_floating_ip(server, public_network_id)
# Verify that we can indeed connect to the server before we mess with
@@ -78,7 +81,7 @@
server, username, private_key,
should_connect=should_connect,
servers_for_debug=[server])
- floating_ip_addr = floating_ip.floating_ip_address
+ floating_ip_addr = floating_ip['floating_ip_address']
# Check FloatingIP status before checking the connectivity
self.check_floating_ip_status(floating_ip, 'ACTIVE')
self.check_public_network_connectivity(floating_ip_addr, username,
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index f5134e5..402a70c 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -25,7 +25,6 @@
from tempest import exceptions
from tempest.lib.common.utils import test_utils
from tempest.scenario import manager
-from tempest.scenario import network_resources
from tempest import test
CONF = config.CONF
@@ -114,7 +113,7 @@
self.port_id = None
if boot_with_port:
# create a port on the network and boot with that
- self.port_id = self._create_port(self.network['id']).id
+ self.port_id = self._create_port(self.network['id'])['id']
self.ports.append({'port': self.port_id})
name = data_utils.rand_name('server-smoke')
@@ -133,30 +132,30 @@
seen_nets = self._list_networks()
seen_names = [n['name'] for n in seen_nets]
seen_ids = [n['id'] for n in seen_nets]
- self.assertIn(self.network.name, seen_names)
- self.assertIn(self.network.id, seen_ids)
+ self.assertIn(self.network['name'], seen_names)
+ self.assertIn(self.network['id'], seen_ids)
if self.subnet:
seen_subnets = self._list_subnets()
seen_net_ids = [n['network_id'] for n in seen_subnets]
seen_subnet_ids = [n['id'] for n in seen_subnets]
- self.assertIn(self.network.id, seen_net_ids)
- self.assertIn(self.subnet.id, seen_subnet_ids)
+ self.assertIn(self.network['id'], seen_net_ids)
+ self.assertIn(self.subnet['id'], seen_subnet_ids)
if self.router:
seen_routers = self._list_routers()
seen_router_ids = [n['id'] for n in seen_routers]
seen_router_names = [n['name'] for n in seen_routers]
- self.assertIn(self.router.name,
+ self.assertIn(self.router['name'],
seen_router_names)
- self.assertIn(self.router.id,
+ self.assertIn(self.router['id'],
seen_router_ids)
def _create_server(self, name, network, port_id=None):
keypair = self.create_keypair()
self.keypairs[keypair['name']] = keypair
security_groups = [{'name': self.security_group['name']}]
- network = {'uuid': network.id}
+ network = {'uuid': network['id']}
if port_id is not None:
network['port'] = port_id
@@ -198,7 +197,7 @@
"""
ssh_login = CONF.validation.image_ssh_user
floating_ip, server = self.floating_ip_tuple
- ip_address = floating_ip.floating_ip_address
+ ip_address = floating_ip['floating_ip_address']
private_key = None
floatingip_status = 'DOWN'
if should_connect:
@@ -239,7 +238,7 @@
def _hotplug_server(self):
old_floating_ip, server = self.floating_ip_tuple
- ip_address = old_floating_ip.floating_ip_address
+ ip_address = old_floating_ip['floating_ip_address']
private_key = self._get_server_key(server)
ssh_client = self.get_remote_client(
ip_address, private_key=private_key)
@@ -250,7 +249,7 @@
old_port = port_list[0]
interface = self.interface_client.create_interface(
server_id=server['id'],
- net_id=self.new_net.id)['interfaceAttachment']
+ net_id=self.new_net['id'])['interfaceAttachment']
self.addCleanup(self.ports_client.wait_for_resource_deletion,
interface['port_id'])
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
@@ -270,9 +269,7 @@
"Old port: %s. Number of new ports: %d" % (
CONF.network.build_timeout, old_port,
len(self.new_port_list)))
- new_port = network_resources.DeletablePort(
- ports_client=self.ports_client,
- **self.new_port_list[0])
+ new_port = self.new_port_list[0]
def check_new_nic():
new_nic_list = self._get_server_nics(ssh_client)
@@ -287,7 +284,8 @@
num, new_nic = self.diff_list[0]
ssh_client.assign_static_ip(nic=new_nic,
- addr=new_port.fixed_ips[0]['ip_address'])
+ addr=new_port['fixed_ips'][0][
+ 'ip_address'])
ssh_client.set_nic_state(nic=new_nic)
def _get_server_nics(self, ssh_client):
@@ -307,7 +305,7 @@
# get all network ports in the new network
internal_ips = (p['fixed_ips'][0]['ip_address'] for p in
self._list_ports(tenant_id=server['tenant_id'],
- network_id=network.id)
+ network_id=network['id'])
if p['device_owner'].startswith('network'))
self._check_server_connectivity(floating_ip,
@@ -335,7 +333,7 @@
def _check_server_connectivity(self, floating_ip, address_list,
should_connect=True):
- ip_address = floating_ip.floating_ip_address
+ ip_address = floating_ip['floating_ip_address']
private_key = self._get_server_key(self.floating_ip_tuple.server)
ssh_source = self.get_remote_client(
ip_address, private_key=private_key)
@@ -451,7 +449,13 @@
self._create_server(name, self.new_net)
self._check_network_internal_connectivity(network=self.new_net,
should_connect=False)
- self.new_subnet.add_to_router(self.router.id)
+ router_id = self.router['id']
+ self.routers_client.add_router_interface(
+ router_id, subnet_id=self.new_subnet['id'])
+
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.routers_client.remove_router_interface,
+ router_id, subnet_id=self.new_subnet['id'])
self._check_network_internal_connectivity(network=self.new_net,
should_connect=True)
@@ -553,7 +557,7 @@
self.check_public_network_connectivity(should_connect=True)
floating_ip, server = self.floating_ip_tuple
- ip_address = floating_ip.floating_ip_address
+ ip_address = floating_ip['floating_ip_address']
private_key = self._get_server_key(server)
ssh_client = self.get_remote_client(
ip_address, private_key=private_key)
@@ -568,9 +572,11 @@
act_serv=servers,
trgt_serv=dns_servers))
- self.subnet.update(dns_nameservers=[alt_dns_server])
+ self.subnet = self.subnets_client.update_subnet(
+ self.subnet['id'], dns_nameservers=[alt_dns_server])['subnet']
+
# asserts that Neutron DB has updated the nameservers
- self.assertEqual([alt_dns_server], self.subnet.dns_nameservers,
+ self.assertEqual([alt_dns_server], self.subnet['dns_nameservers'],
"Failed to update subnet's nameservers")
def check_new_dns_server():
@@ -695,7 +701,8 @@
# NOTE(kevinbenton): we have to use the admin credentials to check
# for the distributed flag because self.router only has a project view.
- admin = self.admin_manager.routers_client.show_router(self.router.id)
+ admin = self.admin_manager.routers_client.show_router(
+ self.router['id'])
if admin['router'].get('distributed', False):
msg = "Rescheduling test does not apply to distributed routers."
raise self.skipException(msg)
@@ -704,16 +711,16 @@
# remove resource from agents
hosting_agents = set(a["id"] for a in
- list_hosts(self.router.id)['agents'])
+ list_hosts(self.router['id'])['agents'])
no_migration = agent_list_alive == hosting_agents
LOG.info("Router will be assigned to {mig} hosting agent".
format(mig="the same" if no_migration else "a new"))
for hosting_agent in hosting_agents:
- unschedule_router(hosting_agent, self.router.id)
+ unschedule_router(hosting_agent, self.router['id'])
self.assertNotIn(hosting_agent,
[a["id"] for a in
- list_hosts(self.router.id)['agents']],
+ list_hosts(self.router['id'])['agents']],
'unscheduling router failed')
# verify resource is un-functional
@@ -730,7 +737,7 @@
router_id=self.router['id'])
self.assertEqual(
target_agent,
- list_hosts(self.router.id)['agents'][0]['id'],
+ list_hosts(self.router['id'])['agents'][0]['id'],
"Router failed to reschedule. Hosting agent doesn't match "
"target agent")
@@ -776,12 +783,12 @@
network_id=self.new_net["id"])
spoof_port = new_ports[0]
private_key = self._get_server_key(server)
- ssh_client = self.get_remote_client(fip.floating_ip_address,
+ ssh_client = self.get_remote_client(fip['floating_ip_address'],
private_key=private_key)
spoof_nic = ssh_client.get_nic_name_by_mac(spoof_port["mac_address"])
name = data_utils.rand_name('peer')
peer = self._create_server(name, self.new_net)
- peer_address = peer['addresses'][self.new_net.name][0]['addr']
+ 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)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index a52d8f9..59ebb7a 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -17,6 +17,7 @@
import six
from tempest import config
+from tempest.lib.common.utils import test_utils
from tempest.scenario import manager
from tempest import test
@@ -82,8 +83,12 @@
ip_version=4)
router = self._get_router(tenant_id=self.tenant_id)
- sub4.add_to_router(router_id=router['id'])
- self.addCleanup(sub4.delete)
+ self.routers_client.add_router_interface(router['id'],
+ subnet_id=sub4['id'])
+
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.routers_client.remove_router_interface,
+ router['id'], subnet_id=sub4['id'])
self.subnets_v6 = []
for _ in range(n_subnets6):
@@ -94,10 +99,14 @@
ipv6_ra_mode=address6_mode,
ipv6_address_mode=address6_mode)
- sub6.add_to_router(router_id=router['id'])
- self.addCleanup(sub6.delete)
- self.subnets_v6.append(sub6)
+ self.routers_client.add_router_interface(router['id'],
+ subnet_id=sub6['id'])
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.routers_client.remove_router_interface,
+ router['id'], subnet_id=sub6['id'])
+
+ self.subnets_v6.append(sub6)
return [self.network, self.network_v6] if dualnet else [self.network]
@staticmethod
@@ -119,12 +128,12 @@
srv = self.create_server(
key_name=self.keypair['name'],
security_groups=[{'name': self.sec_grp['name']}],
- networks=[{'uuid': n.id} for n in networks],
+ networks=[{'uuid': n['id']} for n in networks],
wait_until='ACTIVE')
fip = self.create_floating_ip(thing=srv)
ips = self.define_server_ips(srv=srv)
ssh = self.get_remote_client(
- ip_address=fip.floating_ip_address,
+ ip_address=fip['floating_ip_address'],
username=username)
return ssh, ips, srv["id"]
@@ -139,7 +148,7 @@
"""
ports = [p["mac_address"] for p in
self._list_ports(device_id=sid,
- network_id=self.network_v6.id)]
+ network_id=self.network_v6['id'])]
self.assertEqual(1, len(ports),
message=("Multiple IPv6 ports found on network %s. "
"ports: %s")
@@ -190,11 +199,11 @@
self._check_connectivity(sshv4_1,
ips_from_api_2['6'][i])
self._check_connectivity(sshv4_1,
- self.subnets_v6[i].gateway_ip)
+ self.subnets_v6[i]['gateway_ip'])
self._check_connectivity(sshv4_2,
ips_from_api_1['6'][i])
self._check_connectivity(sshv4_2,
- self.subnets_v6[i].gateway_ip)
+ self.subnets_v6[i]['gateway_ip'])
def _check_connectivity(self, source, dest):
self.assertTrue(
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index adc9008..86185c8 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -227,22 +227,23 @@
seen_names = [n['name'] for n in seen_nets]
seen_ids = [n['id'] for n in seen_nets]
- self.assertIn(tenant.network.name, seen_names)
- self.assertIn(tenant.network.id, seen_ids)
+ self.assertIn(tenant.network['name'], seen_names)
+ self.assertIn(tenant.network['id'], seen_ids)
seen_subnets = [(n['id'], n['cidr'], n['network_id'])
for n in self._list_subnets()]
- mysubnet = (tenant.subnet.id, tenant.subnet.cidr, tenant.network.id)
+ mysubnet = (tenant.subnet['id'], tenant.subnet['cidr'],
+ tenant.network['id'])
self.assertIn(mysubnet, seen_subnets)
seen_routers = self._list_routers()
seen_router_ids = [n['id'] for n in seen_routers]
seen_router_names = [n['name'] for n in seen_routers]
- self.assertIn(tenant.router.name, seen_router_names)
- self.assertIn(tenant.router.id, seen_router_ids)
+ self.assertIn(tenant.router['name'], seen_router_names)
+ self.assertIn(tenant.router['id'], seen_router_ids)
- myport = (tenant.router.id, tenant.subnet.id)
+ myport = (tenant.router['id'], tenant.subnet['id'])
router_ports = [(i['device_id'], i['fixed_ips'][0]['subnet_id']) for i
in self._list_ports()
if self._is_router_port(i)]
@@ -270,7 +271,7 @@
kwargs["scheduler_hints"] = {'different_host': self.servers}
server = self.create_server(
name=name,
- networks=[{'uuid': tenant.network.id}],
+ networks=[{'uuid': tenant.network["id"]}],
key_name=tenant.keypair['name'],
security_groups=security_groups_names,
wait_until='ACTIVE',
@@ -353,10 +354,10 @@
def _get_server_ip(self, server, floating=False):
"""returns the ip (floating/internal) of a server"""
if floating:
- server_ip = self.floating_ips[server['id']].floating_ip_address
+ server_ip = self.floating_ips[server['id']]['floating_ip_address']
else:
server_ip = None
- network_name = self.tenants[server['tenant_id']].network.name
+ network_name = self.tenants[server['tenant_id']].network['name']
if network_name in server['addresses']:
server_ip = server['addresses'][network_name][0]['addr']
return server_ip
@@ -364,7 +365,7 @@
def _connect_to_access_point(self, tenant):
"""create ssh connection to tenant access point"""
access_point_ssh = \
- self.floating_ips[tenant.access_point['id']].floating_ip_address
+ self.floating_ips[tenant.access_point['id']]['floating_ip_address']
private_key = tenant.keypair['private_key']
access_point_ssh = self.get_remote_client(
access_point_ssh, private_key=private_key)
@@ -388,7 +389,7 @@
def _test_in_tenant_allow(self, tenant):
ruleset = dict(
protocol='icmp',
- remote_group_id=tenant.security_groups['default'].id,
+ remote_group_id=tenant.security_groups['default']['id'],
direction='ingress'
)
self._create_security_group_rule(
@@ -464,7 +465,7 @@
for port in port_list if port['fixed_ips']
]
server_ip = self._get_server_ip(tenant.access_point)
- subnet_id = tenant.subnet.id
+ subnet_id = tenant.subnet['id']
self.assertIn((subnet_id, server_ip, mac_addr), port_detail_list)
@test.idempotent_id('e79f879e-debb-440c-a7e4-efeda05b6848')
@@ -545,7 +546,7 @@
# update port with new security group and check connectivity
self.ports_client.update_port(port_id, security_groups=[
- new_tenant.security_groups['new_sg'].id])
+ new_tenant.security_groups['new_sg']['id']])
self._check_connectivity(
access_point=access_point_ssh,
ip=self._get_server_ip(server))
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index a9f2dff..80728dc 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -20,15 +20,12 @@
from tempest import config
from tempest import exceptions
from tempest.scenario import manager
-from tempest.scenario import utils as test_utils
from tempest import test
CONF = config.CONF
LOG = logging.getLogger(__name__)
-load_tests = test_utils.load_tests_input_scenario_utils
-
class TestServerBasicOps(manager.ScenarioTest):
@@ -47,27 +44,10 @@
def setUp(self):
super(TestServerBasicOps, self).setUp()
- # Setup image and flavor the test instance
- # Support both configured and injected values
- if not hasattr(self, 'image_ref'):
- self.image_ref = CONF.compute.image_ref
- if not hasattr(self, 'flavor_ref'):
- self.flavor_ref = CONF.compute.flavor_ref
- self.image_utils = test_utils.ImageUtils(self.manager)
- if not self.image_utils.is_flavor_enough(self.flavor_ref,
- self.image_ref):
- raise self.skipException(
- '{image} does not fit in {flavor}'.format(
- image=self.image_ref, flavor=self.flavor_ref
- )
- )
- self.run_ssh = CONF.validation.run_validation and \
- self.image_utils.is_sshable_image(self.image_ref)
- self.ssh_user = self.image_utils.ssh_user(self.image_ref)
- LOG.debug('Starting test for i:{image}, f:{flavor}. '
- 'Run ssh: {ssh}, user: {ssh_user}'.format(
- image=self.image_ref, flavor=self.flavor_ref,
- ssh=self.run_ssh, ssh_user=self.ssh_user))
+ self.image_ref = CONF.compute.image_ref
+ self.flavor_ref = CONF.compute.flavor_ref
+ self.run_ssh = CONF.validation.run_validation
+ self.ssh_user = CONF.validation.image_ssh_user
def verify_ssh(self, keypair):
if self.run_ssh:
@@ -76,7 +56,7 @@
# Check ssh
self.ssh_client = self.get_remote_client(
ip_address=self.fip,
- username=self.image_utils.ssh_user(self.image_ref),
+ username=self.ssh_user,
private_key=keypair['private_key'])
def verify_metadata(self):
diff --git a/tempest/scenario/utils.py b/tempest/scenario/utils.py
deleted file mode 100644
index 75fd000..0000000
--- a/tempest/scenario/utils.py
+++ /dev/null
@@ -1,184 +0,0 @@
-# Copyright 2013 Hewlett-Packard, Ltd.
-#
-# 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 re
-import string
-import unicodedata
-
-from oslo_serialization import jsonutils as json
-import testscenarios
-import testtools
-
-from tempest import clients
-from tempest.common import credentials_factory as credentials
-from tempest import config
-from tempest.lib.common.utils import misc
-from tempest.lib import exceptions as exc_lib
-
-CONF = config.CONF
-
-
-class ImageUtils(object):
-
- default_ssh_user = 'root'
-
- def __init__(self, os):
- # Load configuration items
- self.ssh_users = json.loads(CONF.input_scenario.ssh_user_regex)
- self.non_ssh_image_pattern = \
- CONF.input_scenario.non_ssh_image_regex
- # Setup clients
- self.compute_images_client = os.compute_images_client
- self.flavors_client = os.flavors_client
-
- def ssh_user(self, image_id):
- _image = self.compute_images_client.show_image(image_id)['image']
- for regex, user in self.ssh_users:
- # First match wins
- if re.match(regex, _image['name']) is not None:
- return user
- else:
- return self.default_ssh_user
-
- def _is_sshable_image(self, image):
- return not re.search(pattern=self.non_ssh_image_pattern,
- string=str(image['name']))
-
- def is_sshable_image(self, image_id):
- _image = self.compute_images_client.show_image(image_id)['image']
- return self._is_sshable_image(_image)
-
- def _is_flavor_enough(self, flavor, image):
- return image['minDisk'] <= flavor['disk']
-
- def is_flavor_enough(self, flavor_id, image_id):
- _image = self.compute_images_client.show_image(image_id)['image']
- _flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
- return self._is_flavor_enough(_flavor, _image)
-
-
-@misc.singleton
-class InputScenarioUtils(object):
-
- """Example usage:
-
- import testscenarios
- (...)
- load_tests = testscenarios.load_tests_apply_scenarios
-
-
- class TestInputScenario(manager.ScenarioTest):
-
- scenario_utils = utils.InputScenarioUtils()
- scenario_flavor = scenario_utils.scenario_flavors
- scenario_image = scenario_utils.scenario_images
- scenarios = testscenarios.multiply_scenarios(scenario_image,
- scenario_flavor)
-
- def test_create_server_metadata(self):
- name = rand_name('instance')
- self.servers_client.create_server(name=name,
- flavorRef=self.flavor_ref,
- imageRef=self.image_ref)
- """
- validchars = "-_.{ascii}{digit}".format(ascii=string.ascii_letters,
- digit=string.digits)
-
- def __init__(self):
- network_resources = {
- 'network': False,
- 'router': False,
- 'subnet': False,
- 'dhcp': False,
- }
- self.cred_provider = credentials.get_credentials_provider(
- name='InputScenarioUtils',
- identity_version=CONF.identity.auth_version,
- network_resources=network_resources)
- os = clients.Manager(self.cred_provider.get_primary_creds())
- self.compute_images_client = os.compute_images_client
- self.flavors_client = os.flavors_client
- self.image_pattern = CONF.input_scenario.image_regex
- self.flavor_pattern = CONF.input_scenario.flavor_regex
-
- def _normalize_name(self, name):
- nname = unicodedata.normalize('NFKD', name).encode('ASCII', 'ignore')
- nname = ''.join(c for c in nname if c in self.validchars)
- return nname
-
- def clear_creds(self):
- self.cred_provider.clear_creds()
-
- @property
- def scenario_images(self):
- """:return: a scenario with name and uuid of images"""
- if not CONF.service_available.glance:
- return []
- if not hasattr(self, '_scenario_images'):
- try:
- images = self.compute_images_client.list_images()['images']
- self._scenario_images = [
- (self._normalize_name(i['name']), dict(image_ref=i['id']))
- for i in images if re.search(self.image_pattern,
- str(i['name']))
- ]
- except Exception:
- self._scenario_images = []
- return self._scenario_images
-
- @property
- def scenario_flavors(self):
- """:return: a scenario with name and uuid of flavors"""
- if not hasattr(self, '_scenario_flavors'):
- try:
- flavors = self.flavors_client.list_flavors()['flavors']
- self._scenario_flavors = [
- (self._normalize_name(f['name']), dict(flavor_ref=f['id']))
- for f in flavors if re.search(self.flavor_pattern,
- str(f['name']))
- ]
- except Exception:
- self._scenario_flavors = []
- return self._scenario_flavors
-
-
-def load_tests_input_scenario_utils(*args):
- """Wrapper for testscenarios to set the scenarios
-
- The purpose is to avoid running a getattr on the CONF object at import.
- """
-
- if getattr(args[0], 'suiteClass', None) is not None:
- loader, standard_tests, pattern = args
- else:
- standard_tests, module, loader = args
- output = None
- scenario_utils = None
- try:
- scenario_utils = InputScenarioUtils()
- scenario_flavor = scenario_utils.scenario_flavors
- scenario_image = scenario_utils.scenario_images
- except (exc_lib.InvalidCredentials, TypeError):
- output = standard_tests
- finally:
- if scenario_utils:
- scenario_utils.clear_creds()
- if output is not None:
- return output
- for test in testtools.iterate_tests(standard_tests):
- setattr(test, 'scenarios', testscenarios.multiply_scenarios(
- scenario_image,
- scenario_flavor))
- return testscenarios.load_tests_apply_scenarios(*args)
diff --git a/tempest/services/baremetal/__init__.py b/tempest/services/baremetal/__init__.py
index e69de29..390f40a 100644
--- a/tempest/services/baremetal/__init__.py
+++ b/tempest/services/baremetal/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.services.baremetal.v1.json.baremetal_client import \
+ BaremetalClient
+
+__all__ = ['BaremetalClient']
diff --git a/tempest/services/data_processing/__init__.py b/tempest/services/data_processing/__init__.py
index e69de29..c49bc5c 100644
--- a/tempest/services/data_processing/__init__.py
+++ b/tempest/services/data_processing/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.services.data_processing.v1_1.data_processing_client import \
+ DataProcessingClient
+
+__all__ = ['DataProcessingClient']
diff --git a/tempest/services/database/__init__.py b/tempest/services/database/__init__.py
index e69de29..9a742d8 100644
--- a/tempest/services/database/__init__.py
+++ b/tempest/services/database/__init__.py
@@ -0,0 +1,23 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.services.database.json.flavors_client import \
+ DatabaseFlavorsClient
+from tempest.services.database.json.limits_client import \
+ DatabaseLimitsClient
+from tempest.services.database.json.versions_client import \
+ DatabaseVersionsClient
+
+__all__ = ['DatabaseFlavorsClient', 'DatabaseLimitsClient',
+ 'DatabaseVersionsClient']
diff --git a/tempest/services/identity/__init__.py b/tempest/services/identity/__init__.py
index e69de29..0e24926 100644
--- a/tempest/services/identity/__init__.py
+++ b/tempest/services/identity/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.services.identity import v2
+from tempest.services.identity import v3
+
+__all__ = ['v2', 'v3']
diff --git a/tempest/services/identity/v2/__init__.py b/tempest/services/identity/v2/__init__.py
index e69de29..6f4ebcf 100644
--- a/tempest/services/identity/v2/__init__.py
+++ b/tempest/services/identity/v2/__init__.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.v2.endpoints_client import EndpointsClient
+from tempest.lib.services.identity.v2.token_client import TokenClient
+from tempest.services.identity.v2.json.identity_client import IdentityClient
+from tempest.services.identity.v2.json.roles_client import RolesClient
+from tempest.services.identity.v2.json.services_client import ServicesClient
+from tempest.services.identity.v2.json.tenants_client import TenantsClient
+from tempest.services.identity.v2.json.users_client import UsersClient
+
+__all__ = ['EndpointsClient', 'TokenClient', 'IdentityClient', 'RolesClient',
+ 'ServicesClient', 'TenantsClient', 'UsersClient']
diff --git a/tempest/services/identity/v2/json/roles_client.py b/tempest/services/identity/v2/json/roles_client.py
index acd97c6..15c8834 100644
--- a/tempest/services/identity/v2/json/roles_client.py
+++ b/tempest/services/identity/v2/json/roles_client.py
@@ -11,6 +11,7 @@
# 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
@@ -30,45 +31,77 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def show_role(self, role_id):
- """Get a role by its id."""
- resp, body = self.get('OS-KSADM/roles/%s' % role_id)
+ def show_role(self, role_id_or_name):
+ """Get a role by its id or name.
+
+ Available params: see
+ http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#showRoleByID
+ OR
+ http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#showRoleByName
+ """
+ resp, body = self.get('OS-KSADM/roles/%s' % role_id_or_name)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def delete_role(self, role_id):
- """Delete a role."""
- resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id))
- self.expected_success(204, resp.status)
- return resp, body
+ def list_roles(self, **params):
+ """Returns roles.
- def list_user_roles(self, tenant_id, user_id):
- """Returns a list of roles assigned to a user for a tenant."""
- url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#listRoles
+ """
+ url = 'OS-KSADM/roles'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def assign_user_role(self, tenant_id, user_id, role_id):
- """Add roles to a user on a tenant."""
+ def delete_role(self, role_id):
+ """Delete a role.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#deleteRole
+ """
+ resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id))
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_user_role_on_project(self, tenant_id, user_id, role_id):
+ """Add roles to a user on a tenant.
+
+ Available params: see
+ http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#grantRoleToUserOnTenant
+ """
resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
(tenant_id, user_id, role_id), "")
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def delete_user_role(self, tenant_id, user_id, role_id):
- """Removes a role assignment for a user on a tenant."""
+ def list_user_roles_on_project(self, tenant_id, user_id, **params):
+ """Returns a list of roles assigned to a user for a tenant."""
+ # TODO(gmann): Need to write API-ref link, Bug# 1592711
+ url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_role_from_user_on_project(self, tenant_id, user_id, role_id):
+ """Removes a role assignment for a user on a tenant.
+
+ Available params: see
+ http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#revokeRoleFromUserOnTenant
+ """
resp, body = self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
(tenant_id, user_id, role_id))
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
-
- def list_roles(self):
- """Returns roles."""
- resp, body = self.get('OS-KSADM/roles')
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/services_client.py b/tempest/services/identity/v2/json/services_client.py
index d8be6c6..4a63d56 100644
--- a/tempest/services/identity/v2/json/services_client.py
+++ b/tempest/services/identity/v2/json/services_client.py
@@ -13,6 +13,7 @@
# 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
@@ -20,14 +21,9 @@
class ServicesClient(rest_client.RestClient):
api_version = "v2.0"
- def create_service(self, name, type, **kwargs):
+ def create_service(self, **kwargs):
"""Create a service."""
- post_body = {
- 'name': name,
- 'type': type,
- 'description': kwargs.get('description')
- }
- post_body = json.dumps({'OS-KSADM:service': post_body})
+ post_body = json.dumps({'OS-KSADM:service': kwargs})
resp, body = self.post('/OS-KSADM/services', post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
@@ -41,9 +37,12 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def list_services(self):
+ def list_services(self, **params):
"""List Service - Returns Services."""
- resp, body = self.get('/OS-KSADM/services')
+ url = '/OS-KSADM/services'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/tenants_client.py b/tempest/services/identity/v2/json/tenants_client.py
index 034938e..77ddaa5 100644
--- a/tempest/services/identity/v2/json/tenants_client.py
+++ b/tempest/services/identity/v2/json/tenants_client.py
@@ -13,6 +13,7 @@
# 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
@@ -20,65 +21,78 @@
class TenantsClient(rest_client.RestClient):
api_version = "v2.0"
- def create_tenant(self, name, **kwargs):
+ def create_tenant(self, **kwargs):
"""Create a tenant
- name (required): New tenant name
- description: Description of new tenant (default is none)
- enabled <true|false>: Initial tenant status (default is true)
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#createTenant
"""
- post_body = {
- 'name': name,
- 'description': kwargs.get('description', ''),
- 'enabled': kwargs.get('enabled', True),
- }
- post_body = json.dumps({'tenant': post_body})
+ post_body = json.dumps({'tenant': kwargs})
resp, body = self.post('tenants', post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def delete_tenant(self, tenant_id):
- """Delete a tenant."""
+ """Delete a tenant.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#deleteTenant
+ """
resp, body = self.delete('tenants/%s' % str(tenant_id))
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
def show_tenant(self, tenant_id):
- """Get tenant details."""
+ """Get tenant details.
+
+ Available params: see
+ http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#admin-showTenantById
+ """
resp, body = self.get('tenants/%s' % str(tenant_id))
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def list_tenants(self):
- """Returns tenants."""
- resp, body = self.get('tenants')
+ def list_tenants(self, **params):
+ """Returns tenants.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#admin-listTenants
+ """
+ url = 'tenants'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def update_tenant(self, tenant_id, **kwargs):
- """Updates a tenant."""
- body = self.show_tenant(tenant_id)['tenant']
- name = kwargs.get('name', body['name'])
- desc = kwargs.get('description', body['description'])
- en = kwargs.get('enabled', body['enabled'])
- post_body = {
- 'id': tenant_id,
- 'name': name,
- 'description': desc,
- 'enabled': en,
- }
- post_body = json.dumps({'tenant': post_body})
+ """Updates a tenant.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#updateTenant
+ """
+ if 'id' not in kwargs:
+ kwargs['id'] = tenant_id
+ post_body = json.dumps({'tenant': kwargs})
resp, body = self.post('tenants/%s' % tenant_id, post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def list_tenant_users(self, tenant_id):
- """List users for a Tenant."""
- resp, body = self.get('/tenants/%s/users' % tenant_id)
+ def list_tenant_users(self, tenant_id, **params):
+ """List users for a Tenant.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#listUsersForTenant
+ """
+ url = '/tenants/%s/users' % tenant_id
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/users_client.py b/tempest/services/identity/v2/json/users_client.py
index 5f8127f..4ea17f9 100644
--- a/tempest/services/identity/v2/json/users_client.py
+++ b/tempest/services/identity/v2/json/users_client.py
@@ -11,6 +11,7 @@
# 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
@@ -18,25 +19,24 @@
class UsersClient(rest_client.RestClient):
api_version = "v2.0"
- def create_user(self, name, password, tenant_id, email, **kwargs):
- """Create a user."""
- post_body = {
- 'name': name,
- 'password': password,
- 'email': email
- }
- if tenant_id is not None:
- post_body['tenantId'] = tenant_id
- if kwargs.get('enabled') is not None:
- post_body['enabled'] = kwargs.get('enabled')
- post_body = json.dumps({'user': post_body})
+ def create_user(self, **kwargs):
+ """Create a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-admin-v2.html#admin-createUser
+ """
+ post_body = json.dumps({'user': kwargs})
resp, body = self.post('users', post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def update_user(self, user_id, **kwargs):
- """Updates a user."""
+ """Updates a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-admin-v2.html#admin-updateUser
+ """
put_body = json.dumps({'user': kwargs})
resp, body = self.put('users/%s' % user_id, put_body)
self.expected_success(200, resp.status)
@@ -44,26 +44,41 @@
return rest_client.ResponseBody(resp, body)
def show_user(self, user_id):
- """GET a user."""
+ """GET a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-admin-v2.html#admin-showUser
+ """
resp, body = self.get("users/%s" % user_id)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def delete_user(self, user_id):
- """Delete a user."""
+ """Delete a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-admin-v2.html#admin-deleteUser
+ """
resp, body = self.delete("users/%s" % user_id)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
- def list_users(self):
- """Get the list of users."""
- resp, body = self.get("users")
+ def list_users(self, **params):
+ """Get the list of users.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-admin-v2.html#admin-listUsers
+ """
+ url = "users"
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def enable_disable_user(self, user_id, **kwargs):
+ def update_user_enabled(self, user_id, **kwargs):
"""Enables or disables a user.
Available params: see http://developer.openstack.org/
@@ -106,7 +121,7 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def create_user_ec2_credentials(self, user_id, **kwargs):
+ def create_user_ec2_credential(self, user_id, **kwargs):
# TODO(piyush): 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.
@@ -117,7 +132,7 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def delete_user_ec2_credentials(self, user_id, access):
+ def delete_user_ec2_credential(self, user_id, access):
resp, body = self.delete('/users/%s/credentials/OS-EC2/%s' %
(user_id, access))
self.expected_success(204, resp.status)
@@ -129,7 +144,7 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def show_user_ec2_credentials(self, user_id, access):
+ def show_user_ec2_credential(self, user_id, access):
resp, body = self.get('/users/%s/credentials/OS-EC2/%s' %
(user_id, access))
self.expected_success(200, resp.status)
diff --git a/tempest/services/identity/v3/__init__.py b/tempest/services/identity/v3/__init__.py
index e69de29..144c5a9 100644
--- a/tempest/services/identity/v3/__init__.py
+++ b/tempest/services/identity/v3/__init__.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.token_client import V3TokenClient
+from tempest.services.identity.v3.json.credentials_client import \
+ CredentialsClient
+from tempest.services.identity.v3.json.domains_client import DomainsClient
+from tempest.services.identity.v3.json.endpoints_client import EndPointsClient
+from tempest.services.identity.v3.json.groups_client import GroupsClient
+from tempest.services.identity.v3.json.identity_client import IdentityClient
+from tempest.services.identity.v3.json.policies_client import PoliciesClient
+from tempest.services.identity.v3.json.projects_client import ProjectsClient
+from tempest.services.identity.v3.json.regions_client import RegionsClient
+from tempest.services.identity.v3.json.roles_client import RolesClient
+from tempest.services.identity.v3.json.services_client import ServicesClient
+from tempest.services.identity.v3.json.trusts_client import TrustsClient
+from tempest.services.identity.v3.json.users_clients import UsersClient
+
+__all__ = ['V3TokenClient', 'CredentialsClient', 'DomainsClient',
+ 'EndPointsClient', 'GroupsClient', 'IdentityClient',
+ 'PoliciesClient', 'ProjectsClient', 'RegionsClient', 'RolesClient',
+ 'ServicesClient', 'TrustsClient', 'UsersClient', ]
diff --git a/tempest/services/identity/v3/json/users_clients.py b/tempest/services/identity/v3/json/users_clients.py
index 3ab8eab..73bd343 100644
--- a/tempest/services/identity/v3/json/users_clients.py
+++ b/tempest/services/identity/v3/json/users_clients.py
@@ -44,7 +44,11 @@
return rest_client.ResponseBody(resp, body)
def update_user(self, user_id, name, **kwargs):
- """Updates a user."""
+ """Updates a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v3.html#updateUser
+ """
body = self.show_user(user_id)['user']
email = kwargs.get('email', body['email'])
en = kwargs.get('enabled', body['enabled'])
diff --git a/tempest/services/image/__init__.py b/tempest/services/image/__init__.py
index e69de29..7ff0886 100644
--- a/tempest/services/image/__init__.py
+++ b/tempest/services/image/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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 import v2
+from tempest.services.image import v1
+
+__all__ = ['v1', 'v2']
diff --git a/tempest/services/image/v1/__init__.py b/tempest/services/image/v1/__init__.py
index e69de29..67dca39 100644
--- a/tempest/services/image/v1/__init__.py
+++ b/tempest/services/image/v1/__init__.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.v1.image_members_client import \
+ ImageMembersClient
+from tempest.services.image.v1.json.images_client import ImagesClient
+
+__all__ = ['ImageMembersClient', 'ImagesClient']
diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py
index 4ffaf3b..ed0a676 100644
--- a/tempest/services/image/v1/json/images_client.py
+++ b/tempest/services/image/v1/json/images_client.py
@@ -16,7 +16,6 @@
import copy
import functools
-from oslo_log import log as logging
from oslo_serialization import jsonutils as json
import six
from six.moves.urllib import parse as urllib
@@ -24,35 +23,12 @@
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
-LOG = logging.getLogger(__name__)
CHUNKSIZE = 1024 * 64 # 64kB
class ImagesClient(rest_client.RestClient):
api_version = "v1"
- def _image_meta_from_headers(self, headers):
- meta = {'properties': {}}
- for key, value in six.iteritems(headers):
- if key.startswith('x-image-meta-property-'):
- _key = key[22:]
- meta['properties'][_key] = value
- elif key.startswith('x-image-meta-'):
- _key = key[13:]
- meta[_key] = value
-
- for key in ['is_public', 'protected', 'deleted']:
- if key in meta:
- meta[key] = meta[key].strip().lower() in ('t', 'true', 'yes',
- '1')
- for key in ['size', 'min_ram', 'min_disk']:
- if key in meta:
- try:
- meta[key] = int(meta[key])
- except ValueError:
- pass
- return meta
-
def _image_meta_to_headers(self, fields):
headers = {}
fields_copy = copy.deepcopy(fields)
@@ -98,9 +74,13 @@
self._http = self._get_http()
return self._http
- def create_image(self, **kwargs):
+ def create_image(self, data=None, **kwargs):
+ """Create an image.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v1.html#createImage-v1
+ """
headers = {}
- data = kwargs.pop('data', None)
headers.update(self._image_meta_to_headers(kwargs))
if data is not None:
@@ -111,9 +91,13 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def update_image(self, image_id, **kwargs):
+ def update_image(self, image_id, data=None, **kwargs):
+ """Update an image.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v1.html#updateImage-v1
+ """
headers = {}
- data = kwargs.pop('data', None)
headers.update(self._image_meta_to_headers(kwargs))
if data is not None:
@@ -146,10 +130,6 @@
if detail:
url += '/detail'
- properties = kwargs.pop('properties', {})
- for key, value in six.iteritems(properties):
- kwargs['property-%s' % key] = value
-
if kwargs.get('changes_since'):
kwargs['changes-since'] = kwargs.pop('changes_since')
@@ -164,9 +144,8 @@
def check_image(self, image_id):
"""Check image metadata."""
url = 'images/%s' % image_id
- resp, __ = self.head(url)
+ resp, body = self.head(url)
self.expected_success(200, resp.status)
- body = self._image_meta_from_headers(resp)
return rest_client.ResponseBody(resp, body)
def show_image(self, image_id):
@@ -178,7 +157,8 @@
def is_resource_deleted(self, id):
try:
- if self.check_image(id)['status'] == 'deleted':
+ resp = self.check_image(id)
+ if resp.response["x-image-meta-status"] == 'deleted':
return True
except lib_exc.NotFound:
return True
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
index e69de29..96fe4a3 100644
--- a/tempest/services/object_storage/__init__.py
+++ b/tempest/services/object_storage/__init__.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.services.object_storage.account_client import AccountClient
+from tempest.services.object_storage.container_client import ContainerClient
+from tempest.services.object_storage.object_client import ObjectClient
+
+__all__ = ['AccountClient', 'ContainerClient', 'ObjectClient']
diff --git a/tempest/services/orchestration/__init__.py b/tempest/services/orchestration/__init__.py
index e69de29..5a1ffcc 100644
--- a/tempest/services/orchestration/__init__.py
+++ b/tempest/services/orchestration/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.services.orchestration.json.orchestration_client import \
+ OrchestrationClient
+
+__all__ = ['OrchestrationClient']
diff --git a/tempest/services/volume/__init__.py b/tempest/services/volume/__init__.py
index e69de29..4d29cdd 100644
--- a/tempest/services/volume/__init__.py
+++ b/tempest/services/volume/__init__.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.services.volume import v1
+from tempest.services.volume import v2
+from tempest.services.volume import v3
+
+__all__ = ['v1', 'v2', 'v3']
diff --git a/tempest/services/volume/base/base_backups_client.py b/tempest/services/volume/base/base_backups_client.py
index 780da7b..3842d66 100644
--- a/tempest/services/volume/base/base_backups_client.py
+++ b/tempest/services/volume/base/base_backups_client.py
@@ -26,7 +26,11 @@
"""Client class to send CRUD Volume backup API requests"""
def create_backup(self, **kwargs):
- """Creates a backup of volume."""
+ """Creates a backup of volume.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#createBackup
+ """
post_body = json.dumps({'backup': kwargs})
resp, body = self.post('backups', post_body)
body = json.loads(body)
@@ -34,7 +38,11 @@
return rest_client.ResponseBody(resp, body)
def restore_backup(self, backup_id, **kwargs):
- """Restore volume from backup."""
+ """Restore volume from backup.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#restoreBackup
+ """
post_body = json.dumps({'restore': kwargs})
resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
body = json.loads(body)
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/services/volume/base/base_snapshots_client.py
index 68503dd..da7bb01 100644
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ b/tempest/services/volume/base/base_snapshots_client.py
@@ -60,7 +60,11 @@
return rest_client.ResponseBody(resp, body)
def update_snapshot(self, snapshot_id, **kwargs):
- """Updates a snapshot."""
+ """Updates a snapshot.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#updateSnapshot
+ """
put_body = json.dumps({'snapshot': kwargs})
resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
body = json.loads(body)
@@ -123,11 +127,11 @@
return rest_client.ResponseBody(resp, body)
def update_snapshot_metadata(self, snapshot_id, **kwargs):
- """Update metadata for the snapshot."""
- # TODO(piyush): 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.
- # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529063
+ """Update metadata for the snapshot.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#updateSnapshotMetadata
+ """
put_body = json.dumps(kwargs)
url = "snapshots/%s/metadata" % str(snapshot_id)
resp, body = self.put(url, put_body)
diff --git a/tempest/services/volume/base/base_v3_client.py b/tempest/services/volume/base/base_v3_client.py
new file mode 100644
index 0000000..ad6f760
--- /dev/null
+++ b/tempest/services/volume/base/base_v3_client.py
@@ -0,0 +1,46 @@
+# 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.lib.common import api_version_utils
+from tempest.lib.common import rest_client
+
+VOLUME_MICROVERSION = None
+
+
+class BaseV3Client(rest_client.RestClient):
+ """Base class to handle Cinder v3 client microversion support."""
+ api_version = 'v3'
+ api_microversion_header_name = 'Openstack-Api-Version'
+
+ def get_headers(self, accept_type=None, send_type=None):
+ headers = super(BaseV3Client, self).get_headers(
+ accept_type=accept_type, send_type=send_type)
+ if VOLUME_MICROVERSION:
+ headers[self.api_microversion_header_name] = ('volume %s' %
+ VOLUME_MICROVERSION)
+ return headers
+
+ def request(self, method, url, extra_headers=False, headers=None,
+ body=None, chunked=False):
+
+ resp, resp_body = super(BaseV3Client, self).request(
+ method, url, extra_headers, headers, body, chunked)
+ if (VOLUME_MICROVERSION and
+ VOLUME_MICROVERSION != api_version_utils.LATEST_MICROVERSION):
+ api_version_utils.assert_version_header_matches_request(
+ self.api_microversion_header_name,
+ 'volume %s' % VOLUME_MICROVERSION,
+ resp)
+ return resp, resp_body
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
old mode 100644
new mode 100755
index 6237745..d694c53
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -96,7 +96,11 @@
return rest_client.ResponseBody(resp, body)
def update_volume(self, volume_id, **kwargs):
- """Updates the Specified Volume."""
+ """Updates the Specified Volume.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#updateVolume
+ """
put_body = json.dumps({'volume': kwargs})
resp, body = self.put('volumes/%s' % volume_id, put_body)
body = json.loads(body)
@@ -119,7 +123,11 @@
return rest_client.ResponseBody(resp, body)
def attach_volume(self, volume_id, **kwargs):
- """Attaches a volume to a given instance on a given mountpoint."""
+ """Attaches a volume to a given instance on a given mountpoint.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#attachVolume
+ """
post_body = json.dumps({'os-attach': kwargs})
url = 'volumes/%s/action' % (volume_id)
resp, body = self.post(url, post_body)
@@ -171,7 +179,11 @@
return 'volume'
def extend_volume(self, volume_id, **kwargs):
- """Extend a volume."""
+ """Extend a volume.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#extendVolume
+ """
post_body = json.dumps({'os-extend': kwargs})
url = 'volumes/%s/action' % (volume_id)
resp, body = self.post(url, post_body)
@@ -179,7 +191,11 @@
return rest_client.ResponseBody(resp, body)
def reset_volume_status(self, volume_id, **kwargs):
- """Reset the Specified Volume's Status."""
+ """Reset the Specified Volume's Status.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#resetVolume
+ """
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)
@@ -202,7 +218,11 @@
return rest_client.ResponseBody(resp, body)
def create_volume_transfer(self, **kwargs):
- """Create a volume transfer."""
+ """Create a volume transfer.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#createVolumeTransfer
+ """
post_body = json.dumps({'transfer': kwargs})
resp, body = self.post('os-volume-transfer', post_body)
body = json.loads(body)
@@ -218,7 +238,11 @@
return rest_client.ResponseBody(resp, body)
def list_volume_transfers(self, **params):
- """List all the volume transfers created."""
+ """List all the volume transfers created.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#listVolumeTransfer
+ """
url = 'os-volume-transfer'
if params:
url += '?%s' % urllib.urlencode(params)
@@ -234,7 +258,11 @@
return rest_client.ResponseBody(resp, body)
def accept_volume_transfer(self, transfer_id, **kwargs):
- """Accept a volume transfer."""
+ """Accept a volume transfer.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#acceptVolumeTransfer
+ """
url = 'os-volume-transfer/%s/accept' % transfer_id
post_body = json.dumps({'accept': kwargs})
resp, body = self.post(url, post_body)
@@ -299,6 +327,28 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
+ def update_volume_image_metadata(self, volume_id, **kwargs):
+ """Update image metadata for the volume.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html
+ #setVolumeimagemetadata
+ """
+ post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
+ url = "volumes/%s/action" % (volume_id)
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_volume_image_metadata(self, volume_id, key_name):
+ """Delete image metadata item for the volume."""
+ post_body = json.dumps({'os-unset_image_metadata': {'key': key_name}})
+ url = "volumes/%s/action" % (volume_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
def retype_volume(self, volume_id, **kwargs):
"""Updates volume with new volume type."""
post_body = json.dumps({'os-retype': kwargs})
diff --git a/tempest/services/volume/v1/__init__.py b/tempest/services/volume/v1/__init__.py
index e69de29..6bdb8c4 100644
--- a/tempest/services/volume/v1/__init__.py
+++ b/tempest/services/volume/v1/__init__.py
@@ -0,0 +1,30 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.services.volume.v1.json.admin.hosts_client import HostsClient
+from tempest.services.volume.v1.json.admin.quotas_client import QuotasClient
+from tempest.services.volume.v1.json.admin.services_client import \
+ ServicesClient
+from tempest.services.volume.v1.json.admin.types_client import TypesClient
+from tempest.services.volume.v1.json.availability_zone_client import \
+ AvailabilityZoneClient
+from tempest.services.volume.v1.json.backups_client import BackupsClient
+from tempest.services.volume.v1.json.extensions_client import ExtensionsClient
+from tempest.services.volume.v1.json.qos_client import QosSpecsClient
+from tempest.services.volume.v1.json.snapshots_client import SnapshotsClient
+from tempest.services.volume.v1.json.volumes_client import VolumesClient
+
+__all__ = ['HostsClient', 'QuotasClient', 'ServicesClient', 'TypesClient',
+ 'AvailabilityZoneClient', 'BackupsClient', 'ExtensionsClient',
+ 'QosSpecsClient', 'SnapshotsClient', 'VolumesClient']
diff --git a/tempest/services/volume/v2/__init__.py b/tempest/services/volume/v2/__init__.py
index e69de29..c75b0e5 100644
--- a/tempest/services/volume/v2/__init__.py
+++ b/tempest/services/volume/v2/__init__.py
@@ -0,0 +1,30 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.services.volume.v2.json.admin.hosts_client import HostsClient
+from tempest.services.volume.v2.json.admin.quotas_client import QuotasClient
+from tempest.services.volume.v2.json.admin.services_client import \
+ ServicesClient
+from tempest.services.volume.v2.json.admin.types_client import TypesClient
+from tempest.services.volume.v2.json.availability_zone_client import \
+ AvailabilityZoneClient
+from tempest.services.volume.v2.json.backups_client import BackupsClient
+from tempest.services.volume.v2.json.extensions_client import ExtensionsClient
+from tempest.services.volume.v2.json.qos_client import QosSpecsClient
+from tempest.services.volume.v2.json.snapshots_client import SnapshotsClient
+from tempest.services.volume.v2.json.volumes_client import VolumesClient
+
+__all__ = ['HostsClient', 'QuotasClient', 'ServicesClient', 'TypesClient',
+ 'AvailabilityZoneClient', 'BackupsClient', 'ExtensionsClient',
+ 'QosSpecsClient', 'SnapshotsClient', 'VolumesClient']
diff --git a/tempest/services/volume/v3/__init__.py b/tempest/services/volume/v3/__init__.py
new file mode 100644
index 0000000..d50098c
--- /dev/null
+++ b/tempest/services/volume/v3/__init__.py
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.services.volume.v3.json.messages_client import MessagesClient
+
+__all__ = ['MessagesClient']
diff --git a/tempest/services/image/v2/json/__init__.py b/tempest/services/volume/v3/json/__init__.py
similarity index 100%
rename from tempest/services/image/v2/json/__init__.py
rename to tempest/services/volume/v3/json/__init__.py
diff --git a/tempest/services/volume/v3/json/messages_client.py b/tempest/services/volume/v3/json/messages_client.py
new file mode 100644
index 0000000..6be6d59
--- /dev/null
+++ b/tempest/services/volume/v3/json/messages_client.py
@@ -0,0 +1,59 @@
+# 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+from tempest.services.volume.base import base_v3_client
+
+
+class MessagesClient(base_v3_client.BaseV3Client):
+ """Client class to send user messages API requests."""
+
+ def show_message(self, message_id):
+ """Show details for a single message."""
+ url = 'messages/%s' % str(message_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_messages(self):
+ """List all messages."""
+ url = 'messages'
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_message(self, message_id):
+ """Delete a single message."""
+ url = 'messages/%s' % str(message_id)
+ resp, body = self.delete(url)
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def is_resource_deleted(self, id):
+ try:
+ self.show_message(id)
+ except lib_exc.NotFound:
+ return True
+ return False
+
+ @property
+ def resource_type(self):
+ """Returns the primary type of resource this client works with."""
+ return 'message'
diff --git a/tempest/test.py b/tempest/test.py
index d31c509..4e06db8 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -39,6 +39,7 @@
from tempest import exceptions
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
LOG = logging.getLogger(__name__)
@@ -539,9 +540,10 @@
if hasattr(cred_provider, credentials_method):
creds = getattr(cred_provider, credentials_method)()
else:
- raise exceptions.InvalidCredentials(
+ raise lib_exc.InvalidCredentials(
"Invalid credentials type %s" % credential_type)
- return cls.client_manager(credentials=creds, service=cls._service)
+ return cls.client_manager(credentials=creds.credentials,
+ service=cls._service)
@classmethod
def clear_credentials(cls):
@@ -640,7 +642,7 @@
credentials.is_admin_available(
identity_version=cls.get_identity_version())):
admin_creds = cred_provider.get_admin_creds()
- admin_manager = clients.Manager(admin_creds)
+ admin_manager = clients.Manager(admin_creds.credentials)
networks_client = admin_manager.compute_networks_client
return fixed_network.get_tenant_network(
cred_provider, networks_client, CONF.compute.fixed_network_name)
diff --git a/tempest/tests/cmd/test_account_generator.py b/tempest/tests/cmd/test_account_generator.py
new file mode 100755
index 0000000..b3931d1
--- /dev/null
+++ b/tempest/tests/cmd/test_account_generator.py
@@ -0,0 +1,346 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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 fixtures
+import mock
+from oslo_config import cfg
+
+from tempest.cmd import account_generator
+from tempest import config
+from tempest.tests import base
+from tempest.tests import fake_config
+from tempest.tests.lib import fake_identity
+
+
+class FakeOpts(object):
+
+ def __init__(self, version=3):
+ self.os_username = 'fake_user'
+ self.os_password = 'fake_password'
+ self.os_project_name = 'fake_project_name'
+ self.os_tenant_name = None
+ self.os_domain_name = 'fake_domain'
+ self.tag = 'fake'
+ self.concurrency = 2
+ self.with_admin = True
+ self.identity_version = version
+ self.accounts = 'fake_accounts.yml'
+
+
+class MockHelpersMixin(object):
+
+ def mock_config_and_opts(self, identity_version):
+ self.useFixture(fake_config.ConfigFixture())
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
+ self.opts = FakeOpts(version=identity_version)
+
+ def mock_resource_creation(self):
+ fake_resource = dict(id='id', name='name')
+ self.user_create_fixture = self.useFixture(fixtures.MockPatch(
+ self.cred_client + '.create_user', return_value=fake_resource))
+ self.useFixture(fixtures.MockPatch(
+ self.cred_client + '.create_project',
+ return_value=fake_resource))
+ self.useFixture(fixtures.MockPatch(
+ self.cred_client + '.assign_user_role'))
+ self.useFixture(fixtures.MockPatch(
+ self.cred_client + '._check_role_exists',
+ return_value=fake_resource))
+ self.useFixture(fixtures.MockPatch(
+ self.dynamic_creds + '._create_network',
+ return_value=fake_resource))
+ self.useFixture(fixtures.MockPatch(
+ self.dynamic_creds + '._create_subnet',
+ return_value=fake_resource))
+ self.useFixture(fixtures.MockPatch(
+ self.dynamic_creds + '._create_router',
+ return_value=fake_resource))
+ self.useFixture(fixtures.MockPatch(
+ self.dynamic_creds + '._add_router_interface',
+ return_value=fake_resource))
+
+ def mock_domains(self):
+ fake_domain_list = {'domains': [{'id': 'fake_domain',
+ 'name': 'Fake_Domain'}]}
+ self.useFixture(fixtures.MockPatch(''.join([
+ 'tempest.services.identity.v3.json.domains_client.'
+ 'DomainsClient.list_domains']),
+ return_value=fake_domain_list))
+ self.useFixture(fixtures.MockPatch(
+ self.cred_client + '.assign_user_role_on_domain'))
+
+
+class TestAccountGeneratorV2(base.TestCase, MockHelpersMixin):
+
+ identity_version = 2
+ identity_response = fake_identity._fake_v2_response
+
+ def setUp(self):
+ super(TestAccountGeneratorV2, self).setUp()
+ self.mock_config_and_opts(self.identity_version)
+ self.useFixture(fixtures.MockPatch(
+ 'tempest.lib.auth.AuthProvider.set_auth',
+ return_value=self.identity_response))
+
+ def test_get_credential_provider(self):
+ cp = account_generator.get_credential_provider(self.opts)
+ admin_creds = cp.default_admin_creds
+ self.assertEqual(self.opts.tag, cp.name)
+ self.assertIn(str(self.opts.identity_version), cp.identity_version)
+ self.assertEqual(self.opts.os_username, admin_creds.username)
+ self.assertEqual(self.opts.os_project_name, admin_creds.tenant_name)
+ self.assertEqual(self.opts.os_password, admin_creds.password)
+ self.assertFalse(hasattr(admin_creds, 'domain_name'))
+
+ def test_get_credential_provider_with_tenant(self):
+ self.opts.os_project_name = None
+ self.opts.os_tenant_name = 'fake_tenant'
+ cp = account_generator.get_credential_provider(self.opts)
+ admin_creds = cp.default_admin_creds
+ self.assertEqual(self.opts.os_tenant_name, admin_creds.tenant_name)
+
+
+class TestAccountGeneratorV3(TestAccountGeneratorV2):
+
+ identity_version = 3
+ identity_response = fake_identity._fake_v3_response
+
+ def setUp(self):
+ super(TestAccountGeneratorV3, self).setUp()
+ fake_domain_list = {'domains': [{'id': 'fake_domain'}]}
+ self.useFixture(fixtures.MockPatch(''.join([
+ 'tempest.services.identity.v3.json.domains_client.'
+ 'DomainsClient.list_domains']),
+ return_value=fake_domain_list))
+
+ def test_get_credential_provider(self):
+ cp = account_generator.get_credential_provider(self.opts)
+ admin_creds = cp.default_admin_creds
+ self.assertEqual(self.opts.tag, cp.name)
+ self.assertIn(str(self.opts.identity_version), cp.identity_version)
+ self.assertEqual(self.opts.os_username, admin_creds.username)
+ self.assertEqual(self.opts.os_project_name, admin_creds.tenant_name)
+ self.assertEqual(self.opts.os_password, admin_creds.password)
+ self.assertEqual(self.opts.os_domain_name, admin_creds.domain_name)
+
+ def test_get_credential_provider_without_domain(self):
+ self.opts.os_domain_name = None
+ cp = account_generator.get_credential_provider(self.opts)
+ admin_creds = cp.default_admin_creds
+ self.assertIsNotNone(admin_creds.domain_name)
+
+
+class TestGenerateResourcesV2(base.TestCase, MockHelpersMixin):
+
+ identity_version = 2
+ identity_response = fake_identity._fake_v2_response
+ cred_client = 'tempest.common.cred_client.V2CredsClient'
+ dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
+
+ def setUp(self):
+ super(TestGenerateResourcesV2, self).setUp()
+ self.mock_config_and_opts(self.identity_version)
+ self.useFixture(fixtures.MockPatch(
+ 'tempest.lib.auth.AuthProvider.set_auth',
+ return_value=self.identity_response))
+ self.cred_provider = account_generator.get_credential_provider(
+ self.opts)
+ self.mock_resource_creation()
+
+ def test_generate_resources_no_admin(self):
+ cfg.CONF.set_default('swift', False, group='service_available')
+ cfg.CONF.set_default('heat', False, group='service_available')
+ cfg.CONF.set_default('operator_role', 'fake_operator',
+ group='object-storage')
+ cfg.CONF.set_default('reseller_admin_role', 'fake_reseller',
+ group='object-storage')
+ cfg.CONF.set_default('stack_owner_role', 'fake_owner',
+ group='orchestration')
+ resources = account_generator.generate_resources(
+ self.cred_provider, admin=False)
+ resource_types = [k for k, _ in resources]
+ # No admin, no heat, no swift, expect two credentials only
+ self.assertEqual(2, len(resources))
+ # Ensure create_user was invoked twice (two distinct users)
+ self.assertEqual(2, self.user_create_fixture.mock.call_count)
+ self.assertIn('primary', resource_types)
+ self.assertIn('alt', resource_types)
+ self.assertNotIn('admin', resource_types)
+ self.assertNotIn(['fake_operator'], resource_types)
+ self.assertNotIn(['fake_reseller'], resource_types)
+ self.assertNotIn(['fake_owner'], resource_types)
+ for resource in resources:
+ self.assertIsNotNone(resource[1].network)
+ self.assertIsNotNone(resource[1].router)
+ self.assertIsNotNone(resource[1].subnet)
+
+ def test_generate_resources_admin(self):
+ cfg.CONF.set_default('swift', False, group='service_available')
+ cfg.CONF.set_default('heat', False, group='service_available')
+ cfg.CONF.set_default('operator_role', 'fake_operator',
+ group='object-storage')
+ cfg.CONF.set_default('reseller_admin_role', 'fake_reseller',
+ group='object-storage')
+ cfg.CONF.set_default('stack_owner_role', 'fake_owner',
+ group='orchestration')
+ resources = account_generator.generate_resources(
+ self.cred_provider, admin=True)
+ resource_types = [k for k, _ in resources]
+ # Admin, no heat, no swift, expect three credentials only
+ self.assertEqual(3, len(resources))
+ # Ensure create_user was invoked 3 times (3 distinct users)
+ self.assertEqual(3, self.user_create_fixture.mock.call_count)
+ self.assertIn('primary', resource_types)
+ self.assertIn('alt', resource_types)
+ self.assertIn('admin', resource_types)
+ self.assertNotIn(['fake_operator'], resource_types)
+ self.assertNotIn(['fake_reseller'], resource_types)
+ self.assertNotIn(['fake_owner'], resource_types)
+ for resource in resources:
+ self.assertIsNotNone(resource[1].network)
+ self.assertIsNotNone(resource[1].router)
+ self.assertIsNotNone(resource[1].subnet)
+
+ def test_generate_resources_swift_heat_admin(self):
+ cfg.CONF.set_default('swift', True, group='service_available')
+ cfg.CONF.set_default('heat', True, group='service_available')
+ cfg.CONF.set_default('operator_role', 'fake_operator',
+ group='object-storage')
+ cfg.CONF.set_default('reseller_admin_role', 'fake_reseller',
+ group='object-storage')
+ cfg.CONF.set_default('stack_owner_role', 'fake_owner',
+ group='orchestration')
+ resources = account_generator.generate_resources(
+ self.cred_provider, admin=True)
+ resource_types = [k for k, _ in resources]
+ # all options on, expect six credentials
+ self.assertEqual(6, len(resources))
+ # Ensure create_user was invoked 6 times (6 distinct users)
+ self.assertEqual(6, self.user_create_fixture.mock.call_count)
+ self.assertIn('primary', resource_types)
+ self.assertIn('alt', resource_types)
+ self.assertIn('admin', resource_types)
+ self.assertIn(['fake_operator'], resource_types)
+ self.assertIn(['fake_reseller'], resource_types)
+ self.assertIn(['fake_owner', 'fake_operator'], resource_types)
+ for resource in resources:
+ self.assertIsNotNone(resource[1].network)
+ self.assertIsNotNone(resource[1].router)
+ self.assertIsNotNone(resource[1].subnet)
+
+
+class TestGenerateResourcesV3(TestGenerateResourcesV2):
+
+ identity_version = 3
+ identity_response = fake_identity._fake_v3_response
+ cred_client = 'tempest.common.cred_client.V3CredsClient'
+
+ def setUp(self):
+ self.mock_domains()
+ super(TestGenerateResourcesV3, self).setUp()
+
+
+class TestDumpAccountsV2(base.TestCase, MockHelpersMixin):
+
+ identity_version = 2
+ identity_response = fake_identity._fake_v2_response
+ cred_client = 'tempest.common.cred_client.V2CredsClient'
+ dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
+ domain_is_in = False
+
+ def setUp(self):
+ super(TestDumpAccountsV2, self).setUp()
+ self.mock_config_and_opts(self.identity_version)
+ self.useFixture(fixtures.MockPatch(
+ 'tempest.lib.auth.AuthProvider.set_auth',
+ return_value=self.identity_response))
+ self.cred_provider = account_generator.get_credential_provider(
+ self.opts)
+ self.mock_resource_creation()
+ cfg.CONF.set_default('swift', True, group='service_available')
+ cfg.CONF.set_default('heat', True, group='service_available')
+ self.resources = account_generator.generate_resources(
+ self.cred_provider, admin=True)
+
+ def test_dump_accounts(self):
+ self.useFixture(fixtures.MockPatch('os.path.exists',
+ return_value=False))
+ mocked_open = mock.mock_open()
+ with mock.patch('{}.open'.format(account_generator.__name__),
+ mocked_open, create=True):
+ with mock.patch('yaml.safe_dump') as yaml_dump_mock:
+ account_generator.setup_logging()
+ account_generator.dump_accounts(self.resources,
+ self.opts.identity_version,
+ self.opts.accounts)
+ mocked_open.assert_called_once_with(self.opts.accounts, 'w')
+ handle = mocked_open()
+ # Ordered args in [0], keyword args in [1]
+ accounts, f = yaml_dump_mock.call_args[0]
+ self.assertEqual(handle, f)
+ self.assertEqual(6, len(accounts))
+ if self.domain_is_in:
+ self.assertIn('domain_name', accounts[0].keys())
+ else:
+ self.assertNotIn('domain_name', accounts[0].keys())
+ self.assertEqual(1, len([x for x in accounts if
+ x.get('types') == ['admin']]))
+ self.assertEqual(3, len([x for x in accounts if 'roles' in x]))
+ for account in accounts:
+ self.assertIn('resources', account)
+ self.assertIn('network', account.get('resources'))
+
+ def test_dump_accounts_existing_file(self):
+ self.useFixture(fixtures.MockPatch('os.path.exists',
+ return_value=True))
+ rename_mock = self.useFixture(fixtures.MockPatch('os.rename')).mock
+ backup_file = '.'.join((self.opts.accounts, 'bak'))
+ mocked_open = mock.mock_open()
+ with mock.patch('{}.open'.format(account_generator.__name__),
+ mocked_open, create=True):
+ with mock.patch('yaml.safe_dump') as yaml_dump_mock:
+ account_generator.setup_logging()
+ account_generator.dump_accounts(self.resources,
+ self.opts.identity_version,
+ self.opts.accounts)
+ rename_mock.assert_called_once_with(self.opts.accounts, backup_file)
+ mocked_open.assert_called_once_with(self.opts.accounts, 'w')
+ handle = mocked_open()
+ # Ordered args in [0], keyword args in [1]
+ accounts, f = yaml_dump_mock.call_args[0]
+ self.assertEqual(handle, f)
+ self.assertEqual(6, len(accounts))
+ if self.domain_is_in:
+ self.assertIn('domain_name', accounts[0].keys())
+ else:
+ self.assertNotIn('domain_name', accounts[0].keys())
+ self.assertEqual(1, len([x for x in accounts if
+ x.get('types') == ['admin']]))
+ self.assertEqual(3, len([x for x in accounts if 'roles' in x]))
+ for account in accounts:
+ self.assertIn('resources', account)
+ self.assertIn('network', account.get('resources'))
+
+
+class TestDumpAccountsV3(TestDumpAccountsV2):
+
+ identity_version = 3
+ identity_response = fake_identity._fake_v3_response
+ cred_client = 'tempest.common.cred_client.V3CredsClient'
+ domain_is_in = True
+
+ def setUp(self):
+ self.mock_domains()
+ super(TestDumpAccountsV3, self).setUp()
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
index 2d0256a..5ec9720 100644
--- a/tempest/tests/cmd/test_javelin.py
+++ b/tempest/tests/cmd/test_javelin.py
@@ -92,7 +92,7 @@
javelin.create_tenants([self.fake_object['name']])
mocked_function = self.fake_client.tenants.create_tenant
- mocked_function.assert_called_once_with(self.fake_object['name'])
+ mocked_function.assert_called_once_with(name=self.fake_object['name'])
def test_create_duplicate_tenant(self):
self.fake_client.tenants.list_tenants.return_value = {'tenants': [
@@ -120,11 +120,12 @@
fake_tenant_id = self.fake_object['tenant']['id']
fake_email = "%s@%s" % (self.fake_object['user'], fake_tenant_id)
mocked_function = self.fake_client.users.create_user
- mocked_function.assert_called_once_with(self.fake_object['name'],
- self.fake_object['password'],
- fake_tenant_id,
- fake_email,
- enabled=True)
+ mocked_function.assert_called_once_with(
+ name=self.fake_object['name'],
+ password=self.fake_object['password'],
+ tenantId=fake_tenant_id,
+ email=fake_email,
+ enabled=True)
def test_create_user_missing_tenant(self):
self.useFixture(mockpatch.Patch(
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
new file mode 100644
index 0000000..dcffd21
--- /dev/null
+++ b/tempest/tests/cmd/test_run.py
@@ -0,0 +1,118 @@
+# Copyright 2015 Hewlett-Packard Development Company, L.P.
+#
+# 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 argparse
+import os
+import shutil
+import subprocess
+import tempfile
+
+import mock
+
+from tempest.cmd import run
+from tempest.tests import base
+
+DEVNULL = open(os.devnull, 'wb')
+
+
+class TestTempestRun(base.TestCase):
+
+ def setUp(self):
+ super(TestTempestRun, self).setUp()
+ self.run_cmd = run.TempestRun(None, None)
+
+ def test_build_options(self):
+ args = mock.Mock(spec=argparse.Namespace)
+ setattr(args, "subunit", True)
+ setattr(args, "parallel", False)
+ setattr(args, "concurrency", 10)
+ options = self.run_cmd._build_options(args)
+ self.assertEqual(['--subunit',
+ '--concurrency=10'],
+ options)
+
+ def test__build_regex_default(self):
+ args = mock.Mock(spec=argparse.Namespace)
+ setattr(args, 'smoke', False)
+ setattr(args, 'regex', '')
+ self.assertEqual('', self.run_cmd._build_regex(args))
+
+ def test__build_regex_smoke(self):
+ args = mock.Mock(spec=argparse.Namespace)
+ setattr(args, "smoke", True)
+ setattr(args, 'regex', '')
+ self.assertEqual('smoke', self.run_cmd._build_regex(args))
+
+ def test__build_regex_regex(self):
+ args = mock.Mock(spec=argparse.Namespace)
+ setattr(args, 'smoke', False)
+ setattr(args, "regex", 'i_am_a_fun_little_regex')
+ self.assertEqual('i_am_a_fun_little_regex',
+ self.run_cmd._build_regex(args))
+
+
+class TestRunReturnCode(base.TestCase):
+ def setUp(self):
+ super(TestRunReturnCode, self).setUp()
+ # Setup test dirs
+ self.directory = tempfile.mkdtemp(prefix='tempest-unit')
+ self.addCleanup(shutil.rmtree, self.directory)
+ self.test_dir = os.path.join(self.directory, 'tests')
+ os.mkdir(self.test_dir)
+ # Setup Test files
+ self.testr_conf_file = os.path.join(self.directory, '.testr.conf')
+ self.setup_cfg_file = os.path.join(self.directory, 'setup.cfg')
+ self.passing_file = os.path.join(self.test_dir, 'test_passing.py')
+ self.failing_file = os.path.join(self.test_dir, 'test_failing.py')
+ self.init_file = os.path.join(self.test_dir, '__init__.py')
+ self.setup_py = os.path.join(self.directory, 'setup.py')
+ shutil.copy('tempest/tests/files/testr-conf', self.testr_conf_file)
+ shutil.copy('tempest/tests/files/passing-tests', self.passing_file)
+ shutil.copy('tempest/tests/files/failing-tests', self.failing_file)
+ shutil.copy('setup.py', self.setup_py)
+ shutil.copy('tempest/tests/files/setup.cfg', self.setup_cfg_file)
+ shutil.copy('tempest/tests/files/__init__.py', self.init_file)
+ # Change directory, run wrapper and check result
+ self.addCleanup(os.chdir, os.path.abspath(os.curdir))
+ os.chdir(self.directory)
+
+ def assertRunExit(self, cmd, expected):
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = p.communicate()
+ msg = ("Running %s got an unexpected returncode\n"
+ "Stdout: %s\nStderr: %s" % (' '.join(cmd), out, err))
+ self.assertEqual(p.returncode, expected, msg)
+
+ def test_tempest_run_passes(self):
+ # Git init is required for the pbr testr command. pbr requires a git
+ # version or an sdist to work. so make the test directory a git repo
+ # too.
+ subprocess.call(['git', 'init'], stderr=DEVNULL)
+ self.assertRunExit(['tempest', 'run', '--regex', 'passing'], 0)
+
+ def test_tempest_run_passes_with_testrepository(self):
+ # Git init is required for the pbr testr command. pbr requires a git
+ # version or an sdist to work. so make the test directory a git repo
+ # too.
+ subprocess.call(['git', 'init'], stderr=DEVNULL)
+ subprocess.call(['testr', 'init'])
+ self.assertRunExit(['tempest', 'run', '--regex', 'passing'], 0)
+
+ def test_tempest_run_fails(self):
+ # Git init is required for the pbr testr command. pbr requires a git
+ # version or an sdist to work. so make the test directory a git repo
+ # too.
+ subprocess.call(['git', 'init'], stderr=DEVNULL)
+ self.assertRunExit(['tempest', 'run'], 1)
diff --git a/tempest/tests/cmd/test_tempest_init.py b/tempest/tests/cmd/test_tempest_init.py
index 685a0b3..031bf4d 100644
--- a/tempest/tests/cmd/test_tempest_init.py
+++ b/tempest/tests/cmd/test_tempest_init.py
@@ -13,7 +13,6 @@
# under the License.
import os
-import shutil
import fixtures
@@ -43,15 +42,12 @@
local_dir = self.useFixture(fixtures.TempDir())
etc_dir_path = os.path.join(local_dir.path, 'etc/')
os.mkdir(etc_dir_path)
- tmp_dir = self.useFixture(fixtures.TempDir())
- config_dir = os.path.join(tmp_dir.path, 'config/')
- shutil.copytree('etc/', config_dir)
init_cmd = init.TempestInit(None, None)
local_sample_conf_file = os.path.join(etc_dir_path,
'tempest.conf.sample')
# Verify no sample config file exist
self.assertFalse(os.path.isfile(local_sample_conf_file))
- init_cmd.generate_sample_config(local_dir.path, config_dir)
+ init_cmd.generate_sample_config(local_dir.path)
# Verify sample config file exist with some content
self.assertTrue(os.path.isfile(local_sample_conf_file))
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
index c4bd7b2..2639d93 100644
--- a/tempest/tests/cmd/test_workspace.py
+++ b/tempest/tests/cmd/test_workspace.py
@@ -41,8 +41,8 @@
stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
return_code = process.returncode
- msg = ("%s failled with:\nstdout: %s\nstderr: %s" % (' '.join(cmd),
- stdout, stderr))
+ msg = ("%s failed with:\nstdout: %s\nstderr: %s" % (' '.join(cmd),
+ stdout, stderr))
self.assertEqual(return_code, expected, msg)
def test_run_workspace_list(self):
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index a7a3a22..e97f65f 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -54,7 +54,7 @@
users_client = v2_users_client
token_client_class = token_client.TokenClient
fake_response = fake_identity._fake_v2_response
- assign_role_on_project = 'assign_user_role'
+ assign_role_on_project = 'create_user_role_on_project'
tenants_client_class = tenants_client.TenantsClient
delete_tenant = 'delete_tenant'
diff --git a/tempest/tests/common/test_image.py b/tempest/tests/common/test_image.py
new file mode 100644
index 0000000..fdd0ae8
--- /dev/null
+++ b/tempest/tests/common/test_image.py
@@ -0,0 +1,40 @@
+# Copyright 2016 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.common import image
+from tempest.lib.common import rest_client
+from tempest.tests import base
+
+
+class TestImage(base.TestCase):
+
+ def test_get_image_meta_from_headers(self):
+ resp = {
+ 'x-image-meta-id': 'ea30c926-0629-4400-bb6e-f8a8da6a4e56',
+ 'x-image-meta-owner': '8f421f9470e645b1b10f5d2db7804924',
+ 'x-image-meta-status': 'queued',
+ 'x-image-meta-name': 'New Http Image'
+ }
+ respbody = rest_client.ResponseBody(resp)
+ observed = image.get_image_meta_from_headers(respbody)
+
+ expected = {
+ 'properties': {},
+ 'id': 'ea30c926-0629-4400-bb6e-f8a8da6a4e56',
+ 'owner': '8f421f9470e645b1b10f5d2db7804924',
+ 'status': 'queued',
+ 'name': 'New Http Image'
+ }
+ self.assertEqual(expected, observed)
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 7d625cf..e59e08f 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -67,14 +67,9 @@
self.ssh_mock = self.useFixture(mockpatch.PatchObject(self.conn,
'ssh_client'))
- def test_hostname_equals_servername_for_expected_names(self):
+ def test_get_hostname(self):
self.ssh_mock.mock.exec_command.return_value = 'fake_hostname'
- self.assertTrue(self.conn.hostname_equals_servername('fake_hostname'))
-
- def test_hostname_equals_servername_for_unexpected_names(self):
- self.ssh_mock.mock.exec_command.return_value = 'fake_hostname'
- self.assertFalse(
- self.conn.hostname_equals_servername('unexpected_hostname'))
+ self.assertEqual(self.conn.get_hostname(), 'fake_hostname')
def test_get_ram_size(self):
free_output = "Mem: 48294 45738 2555 0" \
diff --git a/tempest/tests/lib/fake_identity.py b/tempest/tests/lib/fake_identity.py
index c903e47..831f8b5 100644
--- a/tempest/tests/lib/fake_identity.py
+++ b/tempest/tests/lib/fake_identity.py
@@ -100,7 +100,8 @@
],
"type": "compute",
- "id": "fake_compute_endpoint"
+ "id": "fake_compute_endpoint",
+ "name": "nova"
}
CATALOG_V3 = [COMPUTE_ENDPOINTS_V3, ]
diff --git a/tempest/tests/lib/services/identity/v2/test_endpoints_client.py b/tempest/tests/lib/services/identity/v2/test_endpoints_client.py
new file mode 100644
index 0000000..7d2cac2
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_endpoints_client.py
@@ -0,0 +1,99 @@
+# Copyright 2016 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.identity.v2 import endpoints_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestEndpointsClient(base.BaseServiceTest):
+ FAKE_CREATE_ENDPOINT = {
+ "endpoint": {
+ "id": 1,
+ "tenantId": 1,
+ "region": "North",
+ "type": "compute",
+ "publicURL": "https://compute.north.public.com/v1",
+ "internalURL": "https://compute.north.internal.com/v1",
+ "adminURL": "https://compute.north.internal.com/v1"
+ }
+ }
+
+ FAKE_LIST_ENDPOINTS = {
+ "endpoints": [
+ {
+ "id": 1,
+ "tenantId": "1",
+ "region": "North",
+ "type": "compute",
+ "publicURL": "https://compute.north.public.com/v1",
+ "internalURL": "https://compute.north.internal.com/v1",
+ "adminURL": "https://compute.north.internal.com/v1"
+ },
+ {
+ "id": 2,
+ "tenantId": "1",
+ "region": "South",
+ "type": "compute",
+ "publicURL": "https://compute.north.public.com/v1",
+ "internalURL": "https://compute.north.internal.com/v1",
+ "adminURL": "https://compute.north.internal.com/v1"
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestEndpointsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = endpoints_client.EndpointsClient(fake_auth,
+ 'identity', 'regionOne')
+
+ def _test_create_endpoint(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_endpoint,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_ENDPOINT,
+ bytes_body,
+ service_id="b344506af7644f6794d9cb316600b020",
+ region="region-demo",
+ publicurl="https://compute.north.public.com/v1",
+ adminurl="https://compute.north.internal.com/v1",
+ internalurl="https://compute.north.internal.com/v1")
+
+ def _test_list_endpoints(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_endpoints,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ENDPOINTS,
+ bytes_body)
+
+ def test_create_endpoint_with_str_body(self):
+ self._test_create_endpoint()
+
+ def test_create_endpoint_with_bytes_body(self):
+ self._test_create_endpoint(bytes_body=True)
+
+ def test_list_endpoints_with_str_body(self):
+ self._test_list_endpoints()
+
+ def test_list_endpoints_with_bytes_body(self):
+ self._test_list_endpoints(bytes_body=True)
+
+ def test_delete_endpoint(self):
+ self.check_service_client_function(
+ self.client.delete_endpoint,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ endpoint_id="b344506af7644f6794d9cb316600b020",
+ status=204)
diff --git a/tempest/tests/lib/services/identity/v2/test_token_client.py b/tempest/tests/lib/services/identity/v2/test_token_client.py
index 7925152..dfce9b3 100644
--- a/tempest/tests/lib/services/identity/v2/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_token_client.py
@@ -25,9 +25,6 @@
class TestTokenClientV2(base.TestCase):
- def setUp(self):
- super(TestTokenClientV2, self).setUp()
-
def test_init_without_authurl(self):
self.assertRaises(exceptions.IdentityError,
token_client.TokenClient, None)
diff --git a/tempest/tests/lib/services/identity/v3/test_token_client.py b/tempest/tests/lib/services/identity/v3/test_token_client.py
index e9ef740..9f4b4cc 100644
--- a/tempest/tests/lib/services/identity/v3/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_token_client.py
@@ -25,9 +25,6 @@
class TestTokenClientV3(base.TestCase):
- def setUp(self):
- super(TestTokenClientV3, self).setUp()
-
def test_init_without_authurl(self):
self.assertRaises(exceptions.IdentityError,
token_client.V3TokenClient, None)
diff --git a/tempest/services/image/v2/__init__.py b/tempest/tests/lib/services/image/__init__.py
similarity index 100%
copy from tempest/services/image/v2/__init__.py
copy to tempest/tests/lib/services/image/__init__.py
diff --git a/tempest/services/image/v2/__init__.py b/tempest/tests/lib/services/image/v1/__init__.py
similarity index 100%
copy from tempest/services/image/v2/__init__.py
copy to tempest/tests/lib/services/image/v1/__init__.py
diff --git a/tempest/tests/lib/services/image/v1/test_image_members_client.py b/tempest/tests/lib/services/image/v1/test_image_members_client.py
new file mode 100644
index 0000000..a5a6128
--- /dev/null
+++ b/tempest/tests/lib/services/image/v1/test_image_members_client.py
@@ -0,0 +1,84 @@
+# Copyright 2016 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.v1 import image_members_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestImageMembersClient(base.BaseServiceTest):
+ FAKE_LIST_IMAGE_MEMBERS = {
+ "members": [
+ {
+ "created_at": "2013-10-07T17:58:03Z",
+ "image_id": "dbc999e3-c52f-4200-bedd-3b18fe7f87fe",
+ "member_id": "123456789",
+ "status": "pending",
+ "updated_at": "2013-10-07T17:58:03Z"
+ },
+ {
+ "created_at": "2013-10-07T17:58:55Z",
+ "image_id": "dbc999e3-c52f-4200-bedd-3b18fe7f87fe",
+ "member_id": "987654321",
+ "status": "accepted",
+ "updated_at": "2013-10-08T12:08:55Z"
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestImageMembersClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = image_members_client.ImageMembersClient(fake_auth,
+ 'image',
+ 'regionOne')
+
+ def _test_list_image_members(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_image_members,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_IMAGE_MEMBERS,
+ bytes_body,
+ image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e")
+
+ def _test_create_image_member(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_image_member,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ {},
+ bytes_body,
+ image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+ member_id="8989447062e04a818baf9e073fd04fa7",
+ status=204)
+
+ def test_list_image_members_with_str_body(self):
+ self._test_list_image_members()
+
+ def test_list_image_members_with_bytes_body(self):
+ self._test_list_image_members(bytes_body=True)
+
+ def test_create_image_member_with_str_body(self):
+ self._test_create_image_member()
+
+ def test_create_image_member_with_bytes_body(self):
+ self._test_create_image_member(bytes_body=True)
+
+ def test_delete_image_member(self):
+ self.check_service_client_function(
+ self.client.delete_image_member,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+ member_id="8989447062e04a818baf9e073fd04fa7",
+ status=204)
diff --git a/tempest/services/image/v2/__init__.py b/tempest/tests/lib/services/image/v2/__init__.py
similarity index 100%
rename from tempest/services/image/v2/__init__.py
rename to tempest/tests/lib/services/image/v2/__init__.py
diff --git a/tempest/tests/lib/services/image/v2/test_image_members_client.py b/tempest/tests/lib/services/image/v2/test_image_members_client.py
new file mode 100644
index 0000000..703b6e1
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_image_members_client.py
@@ -0,0 +1,90 @@
+# Copyright 2016 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 image_members_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestImageMembersClient(base.BaseServiceTest):
+ FAKE_CREATE_SHOW_UPDATE_IMAGE_MEMBER = {
+ "status": "pending",
+ "created_at": "2013-11-26T07:21:21Z",
+ "updated_at": "2013-11-26T07:21:21Z",
+ "image_id": "0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+ "member_id": "8989447062e04a818baf9e073fd04fa7",
+ "schema": "/v2/schemas/member"
+ }
+
+ def setUp(self):
+ super(TestImageMembersClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = image_members_client.ImageMembersClient(fake_auth,
+ 'image',
+ 'regionOne')
+
+ def _test_show_image_member(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_image_member,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CREATE_SHOW_UPDATE_IMAGE_MEMBER,
+ bytes_body,
+ image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+ member_id="8989447062e04a818baf9e073fd04fa7")
+
+ def _test_create_image_member(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_image_member,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_SHOW_UPDATE_IMAGE_MEMBER,
+ bytes_body,
+ image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+ member_id="8989447062e04a818baf9e073fd04fa7")
+
+ def _test_update_image_member(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_image_member,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_CREATE_SHOW_UPDATE_IMAGE_MEMBER,
+ bytes_body,
+ image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+ member_id="8989447062e04a818baf9e073fd04fa7",
+ schema="/v2/schemas/member2")
+
+ def test_show_image_member_with_str_body(self):
+ self._test_show_image_member()
+
+ def test_show_image_member_with_bytes_body(self):
+ self._test_show_image_member(bytes_body=True)
+
+ def test_create_image_member_with_str_body(self):
+ self._test_create_image_member()
+
+ def test_create_image_member_with_bytes_body(self):
+ self._test_create_image_member(bytes_body=True)
+
+ def test_delete_image_member(self):
+ self.check_service_client_function(
+ self.client.delete_image_member,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+ member_id="8989447062e04a818baf9e073fd04fa7",
+ status=204)
+
+ def test_update_image_member_with_str_body(self):
+ self._test_update_image_member()
+
+ def test_update_image_member_with_bytes_body(self):
+ self._test_update_image_member(bytes_body=True)
diff --git a/tempest/tests/lib/services/image/v2/test_images_client.py b/tempest/tests/lib/services/image/v2/test_images_client.py
new file mode 100644
index 0000000..9648985
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_images_client.py
@@ -0,0 +1,111 @@
+# Copyright 2016 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 images_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestImagesClient(base.BaseServiceTest):
+ FAKE_CREATE_UPDATE_SHOW_IMAGE = {
+ "id": "e485aab9-0907-4973-921c-bb6da8a8fcf8",
+ "name": u"\u2740(*\xb4\u25e2`*)\u2740",
+ "status": "active",
+ "visibility": "public",
+ "size": 2254249,
+ "checksum": "2cec138d7dae2aa59038ef8c9aec2390",
+ "tags": [
+ "fedora",
+ "beefy"
+ ],
+ "created_at": "2012-08-10T19:23:50Z",
+ "updated_at": "2012-08-12T11:11:33Z",
+ "self": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea",
+ "file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file",
+ "schema": "/v2/schemas/image",
+ "owner": None,
+ "min_ram": None,
+ "min_disk": None,
+ "disk_format": None,
+ "virtual_size": None,
+ "container_format": None
+ }
+
+ def setUp(self):
+ super(TestImagesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = images_client.ImagesClient(fake_auth,
+ 'image', 'regionOne')
+
+ def _test_update_image(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_image,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_CREATE_UPDATE_SHOW_IMAGE,
+ bytes_body,
+ image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8",
+ patch=[{"op": "add", "path": "/a/b/c", "value": ["foo", "bar"]}])
+
+ def _test_create_image(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_image,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_UPDATE_SHOW_IMAGE,
+ bytes_body,
+ name="virtual machine image",
+ status=201)
+
+ def _test_show_image(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_image,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CREATE_UPDATE_SHOW_IMAGE,
+ bytes_body,
+ image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8")
+
+ def test_create_image_with_str_body(self):
+ self._test_create_image()
+
+ def test_create_image_with_bytes_body(self):
+ self._test_create_image(bytes_body=True)
+
+ def test_update_image_with_str_body(self):
+ self._test_update_image()
+
+ def test_update_image_with_bytes_body(self):
+ self._test_update_image(bytes_body=True)
+
+ def test_deactivate_image(self):
+ self.check_service_client_function(
+ self.client.deactivate_image,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
+
+ def test_reactivate_image(self):
+ self.check_service_client_function(
+ self.client.reactivate_image,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
+
+ def test_delete_image(self):
+ self.check_service_client_function(
+ self.client.delete_image,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
+
+ def test_show_image_with_str_body(self):
+ self._test_show_image()
+
+ def test_show_image_with_bytes_body(self):
+ self._test_show_image(bytes_body=True)
diff --git a/tempest/tests/lib/services/image/v2/test_namespaces_client.py b/tempest/tests/lib/services/image/v2/test_namespaces_client.py
new file mode 100644
index 0000000..4cb9d01
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_namespaces_client.py
@@ -0,0 +1,93 @@
+# Copyright 2016 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 namespaces_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNamespacesClient(base.BaseServiceTest):
+ FAKE_CREATE_SHOW_NAMESPACE = {
+ "namespace": "OS::Compute::Hypervisor",
+ "visibility": "public",
+ "description": "Tempest",
+ "display_name": u"\u2740(*\xb4\u25e1`*)\u2740",
+ "protected": True
+ }
+
+ FAKE_UPDATE_NAMESPACE = {
+ "namespace": "OS::Compute::Hypervisor",
+ "visibility": "public",
+ "description": "Tempest",
+ "display_name": u"\u2740(*\xb4\u25e2`*)\u2740",
+ "protected": True
+ }
+
+ def setUp(self):
+ super(TestNamespacesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = namespaces_client.NamespacesClient(fake_auth,
+ 'image', 'regionOne')
+
+ def _test_show_namespace(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_namespace,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CREATE_SHOW_NAMESPACE,
+ bytes_body,
+ namespace="OS::Compute::Hypervisor")
+
+ def _test_create_namespace(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_namespace,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_SHOW_NAMESPACE,
+ bytes_body,
+ namespace="OS::Compute::Hypervisor",
+ visibility="public", description="Tempest",
+ display_name=u"\u2740(*\xb4\u25e1`*)\u2740", protected=True,
+ status=201)
+
+ def _test_update_namespace(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_namespace,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_UPDATE_NAMESPACE,
+ bytes_body,
+ namespace="OS::Compute::Hypervisor",
+ display_name=u"\u2740(*\xb4\u25e2`*)\u2740", protected=True)
+
+ def test_show_namespace_with_str_body(self):
+ self._test_show_namespace()
+
+ def test_show_namespace_with_bytes_body(self):
+ self._test_show_namespace(bytes_body=True)
+
+ def test_create_namespace_with_str_body(self):
+ self._test_create_namespace()
+
+ def test_create_namespace_with_bytes_body(self):
+ self._test_create_namespace(bytes_body=True)
+
+ def test_delete_namespace(self):
+ self.check_service_client_function(
+ self.client.delete_namespace,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {}, namespace="OS::Compute::Hypervisor", status=204)
+
+ def test_update_namespace_with_str_body(self):
+ self._test_update_namespace()
+
+ def test_update_namespace_with_bytes_body(self):
+ self._test_update_namespace(bytes_body=True)
diff --git a/tempest/tests/lib/services/image/v2/test_resource_types_client.py b/tempest/tests/lib/services/image/v2/test_resource_types_client.py
new file mode 100644
index 0000000..2e3b117
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_resource_types_client.py
@@ -0,0 +1,69 @@
+# Copyright 2016 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 resource_types_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestResouceTypesClient(base.BaseServiceTest):
+ FAKE_LIST_RESOURCETYPES = {
+ "resource_types": [
+ {
+ "created_at": "2014-08-28T18:13:04Z",
+ "name": "OS::Glance::Image",
+ "updated_at": "2014-08-28T18:13:04Z"
+ },
+ {
+ "created_at": "2014-08-28T18:13:04Z",
+ "name": "OS::Cinder::Volume",
+ "updated_at": "2014-08-28T18:13:04Z"
+ },
+ {
+ "created_at": "2014-08-28T18:13:04Z",
+ "name": "OS::Nova::Flavor",
+ "updated_at": "2014-08-28T18:13:04Z"
+ },
+ {
+ "created_at": "2014-08-28T18:13:04Z",
+ "name": "OS::Nova::Aggregate",
+ "updated_at": "2014-08-28T18:13:04Z"
+ },
+ {
+ "created_at": "2014-08-28T18:13:04Z",
+ "name": u"\u2740(*\xb4\u25e1`*)\u2740",
+ "updated_at": "2014-08-28T18:13:04Z"
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestResouceTypesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = resource_types_client.ResourceTypesClient(fake_auth,
+ 'image',
+ 'regionOne')
+
+ def _test_list_resouce_types(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_resource_types,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_RESOURCETYPES,
+ bytes_body)
+
+ def test_list_resouce_types_with_str_body(self):
+ self._test_list_resouce_types()
+
+ def test_list_resouce_types_with_bytes_body(self):
+ self._test_list_resouce_types(bytes_body=True)
diff --git a/tempest/tests/lib/services/image/v2/test_schemas_client.py b/tempest/tests/lib/services/image/v2/test_schemas_client.py
new file mode 100644
index 0000000..4c4b86a
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_schemas_client.py
@@ -0,0 +1,96 @@
+# Copyright 2016 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 schemas_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSchemasClient(base.BaseServiceTest):
+ FAKE_SHOW_SCHEMA = {
+ "links": [
+ {
+ "href": "{schema}",
+ "rel": "describedby"
+ }
+ ],
+ "name": "members",
+ "properties": {
+ "members": {
+ "items": {
+ "name": "member",
+ "properties": {
+ "created_at": {
+ "description": ("Date and time of image member"
+ " creation"),
+ "type": "string"
+ },
+ "image_id": {
+ "description": "An identifier for the image",
+ "pattern": ("^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}"
+ "-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}"
+ "-([0-9a-fA-F]){12}$"),
+ "type": "string"
+ },
+ "member_id": {
+ "description": ("An identifier for the image"
+ " member (tenantId)"),
+ "type": "string"
+ },
+ "schema": {
+ "type": "string"
+ },
+ "status": {
+ "description": "The status of this image member",
+ "enum": [
+ "pending",
+ "accepted",
+ "rejected"
+ ],
+ "type": "string"
+ },
+ "updated_at": {
+ "description": ("Date and time of last"
+ " modification of image member"),
+ "type": "string"
+ }
+ }
+ },
+ "type": "array"
+ },
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+
+ def setUp(self):
+ super(TestSchemasClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = schemas_client.SchemasClient(fake_auth,
+ 'image', 'regionOne')
+
+ def _test_show_schema(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_schema,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_SHOW_SCHEMA,
+ bytes_body,
+ schema="member")
+
+ def test_show_schema_with_str_body(self):
+ self._test_show_schema()
+
+ def test_show_schema_with_bytes_body(self):
+ self._test_show_schema(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_versions_client.py b/tempest/tests/lib/services/network/test_versions_client.py
new file mode 100644
index 0000000..715176b
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_versions_client.py
@@ -0,0 +1,77 @@
+# Copyright 2016 VMware, 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 copy
+
+from tempest.lib.services.network.versions_client import NetworkVersionsClient
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNetworkVersionsClient(base.BaseServiceTest):
+
+ FAKE_INIT_VERSION = {
+ "version": {
+ "id": "v2.0",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2.0/",
+ "rel": "self"
+ },
+ {
+ "href": "http://docs.openstack.org/",
+ "rel": "describedby",
+ "type": "text/html"
+ }
+ ],
+ "status": "CURRENT",
+ "updated": "2013-07-23T11:33:21Z",
+ "version": "2.0",
+ "min_version": "2.0"
+ }
+ }
+
+ FAKE_VERSIONS_INFO = {
+ "versions": [FAKE_INIT_VERSION["version"]]
+ }
+
+ FAKE_VERSION_INFO = copy.deepcopy(FAKE_INIT_VERSION)
+
+ FAKE_VERSION_INFO["version"]["media-types"] = [
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.network+json;version=2.0"
+ }
+ ]
+
+ def setUp(self):
+ super(TestNetworkVersionsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.versions_client = (
+ NetworkVersionsClient
+ (fake_auth, 'compute', 'regionOne'))
+
+ def _test_versions_client(self, bytes_body=False):
+ self.check_service_client_function(
+ self.versions_client.list_versions,
+ 'tempest.lib.common.rest_client.RestClient.raw_request',
+ self.FAKE_VERSIONS_INFO,
+ bytes_body,
+ 200)
+
+ def test_list_versions_client_with_str_body(self):
+ self._test_versions_client()
+
+ def test_list_versions_client_with_bytes_body(self):
+ self._test_versions_client(bytes_body=True)
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index c253187..12590a3 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -360,6 +360,58 @@
self.assertRaises(exceptions.EndpointNotFound,
self._test_base_url_helper, None, self.filters)
+ def test_base_url_with_known_name(self):
+ """If name and service is known, return the endpoint."""
+ self.filters = {
+ 'service': 'compute',
+ 'endpoint_type': 'publicURL',
+ 'region': 'FakeRegion',
+ 'name': 'nova'
+ }
+ expected = self._get_result_url_from_endpoint(
+ self._endpoints[0]['endpoints'][1])
+ self._test_base_url_helper(expected, self.filters)
+
+ def test_base_url_with_known_name_and_unknown_servce(self):
+ """Test with Known Name and Unknown service
+
+ If the name is known but the service is unknown, raise an exception.
+ """
+ self.filters = {
+ 'service': 'AintNoBodyKnowThatService',
+ 'endpoint_type': 'publicURL',
+ 'region': 'FakeRegion',
+ 'name': 'AintNoBodyKnowThatName'
+ }
+ self.assertRaises(exceptions.EndpointNotFound,
+ self._test_base_url_helper, None, self.filters)
+
+ def test_base_url_with_unknown_name_and_known_service(self):
+ """Test with Unknown Name and Known Service
+
+ If the name is unknown, raise an exception. Note that filtering by
+ name is only successful service exists.
+ """
+
+ self.filters = {
+ 'service': 'compute',
+ 'endpoint_type': 'publicURL',
+ 'region': 'FakeRegion',
+ 'name': 'AintNoBodyKnowThatName'
+ }
+ self.assertRaises(exceptions.EndpointNotFound,
+ self._test_base_url_helper, None, self.filters)
+
+ def test_base_url_without_name(self):
+ self.filters = {
+ 'service': 'compute',
+ 'endpoint_type': 'publicURL',
+ 'region': 'FakeRegion',
+ }
+ expected = self._get_result_url_from_endpoint(
+ self._endpoints[0]['endpoints'][1])
+ self._test_base_url_helper(expected, self.filters)
+
def test_base_url_with_api_version_filter(self):
self.filters = {
'service': 'compute',
@@ -750,3 +802,12 @@
_, auth_data = self.auth_provider.get_auth()
self.assertIn('domain', auth_data)
self.assertNotIn('project', auth_data)
+
+
+class TestGetCredentials(base.TestCase):
+
+ def test_invalid_identity_version(self):
+ with testtools.ExpectedException(exceptions.InvalidIdentityVersion,
+ '.* v1 .*'):
+ auth.get_credentials('http://localhost/identity/v3',
+ identity_version='v1')
diff --git a/tempest/tests/lib/test_rest_client.py b/tempest/tests/lib/test_rest_client.py
index 106a1e5..057f57b 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/test_rest_client.py
@@ -633,6 +633,7 @@
expected = {'api_version': 'v1',
'endpoint_type': 'publicURL',
'region': None,
+ 'name': None,
'service': None,
'skip_path': True}
self.rest_client.skip_path()
@@ -643,6 +644,7 @@
expected = {'api_version': 'v1',
'endpoint_type': 'publicURL',
'region': None,
+ 'name': None,
'service': None}
self.assertEqual(expected, self.rest_client.filters)
diff --git a/tempest/services/image/v2/__init__.py b/tempest/tests/services/identity/__init__.py
similarity index 100%
copy from tempest/services/image/v2/__init__.py
copy to tempest/tests/services/identity/__init__.py
diff --git a/tempest/services/image/v2/__init__.py b/tempest/tests/services/identity/v2/__init__.py
similarity index 100%
copy from tempest/services/image/v2/__init__.py
copy to tempest/tests/services/identity/v2/__init__.py
diff --git a/tempest/tests/services/identity/v2/test_roles_client.py b/tempest/tests/services/identity/v2/test_roles_client.py
new file mode 100644
index 0000000..e36ec18
--- /dev/null
+++ b/tempest/tests/services/identity/v2/test_roles_client.py
@@ -0,0 +1,141 @@
+# Copyright 2016 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.services.identity.v2.json import roles_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRolesClient(base.BaseServiceTest):
+ FAKE_ROLE_INFO = {
+ "role": {
+ "id": "1",
+ "name": "test",
+ "description": "test_description"
+ }
+ }
+
+ FAKE_LIST_ROLES = {
+ "roles": [
+ {
+ "id": "1",
+ "name": "test",
+ "description": "test_description"
+ },
+ {
+ "id": "2",
+ "name": "test2",
+ "description": "test2_description"
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestRolesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = roles_client.RolesClient(fake_auth,
+ 'identity', 'regionOne')
+
+ def _test_create_role(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_role,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_ROLE_INFO,
+ bytes_body,
+ id="1",
+ name="test",
+ description="test_description")
+
+ def _test_show_role(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_role,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_ROLE_INFO,
+ bytes_body,
+ role_id_or_name="1")
+
+ def _test_list_roles(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_roles,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ROLES,
+ bytes_body)
+
+ def _test_create_user_role_on_project(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_user_role_on_project,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_ROLE_INFO,
+ bytes_body,
+ tenant_id="b344506af7644f6794d9cb316600b020",
+ user_id="123",
+ role_id="1234",
+ status=200)
+
+ def _test_list_user_roles_on_project(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_user_roles_on_project,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ROLES,
+ bytes_body,
+ tenant_id="b344506af7644f6794d9cb316600b020",
+ user_id="123")
+
+ def test_create_role_with_str_body(self):
+ self._test_create_role()
+
+ def test_create_role_with_bytes_body(self):
+ self._test_create_role(bytes_body=True)
+
+ def test_show_role_with_str_body(self):
+ self._test_show_role()
+
+ def test_show_role_with_bytes_body(self):
+ self._test_show_role(bytes_body=True)
+
+ def test_list_roles_with_str_body(self):
+ self._test_list_roles()
+
+ def test_list_roles_with_bytes_body(self):
+ self._test_list_roles(bytes_body=True)
+
+ def test_delete_role(self):
+ self.check_service_client_function(
+ self.client.delete_role,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ role_id="1",
+ status=204)
+
+ def test_create_user_role_on_project_with_str_body(self):
+ self._test_create_user_role_on_project()
+
+ def test_create_user_role_on_project_with_bytes_body(self):
+ self._test_create_user_role_on_project(bytes_body=True)
+
+ def test_list_user_roles_on_project_with_str_body(self):
+ self._test_list_user_roles_on_project()
+
+ def test_list_user_roles_on_project_with_bytes_body(self):
+ self._test_list_user_roles_on_project(bytes_body=True)
+
+ def test_delete_role_from_user_on_project(self):
+ self.check_service_client_function(
+ self.client.delete_role_from_user_on_project,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ tenant_id="b344506af7644f6794d9cb316600b020",
+ user_id="123",
+ role_id="1234",
+ status=204)
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
index dc355b4..01b8a72 100644
--- a/tempest/tests/test_base_test.py
+++ b/tempest/tests/test_base_test.py
@@ -66,7 +66,7 @@
test.BaseTestCase.get_tenant_network()
mock_man.assert_called_once_with(
- mock_prov.get_admin_creds.return_value)
+ mock_prov.get_admin_creds.return_value.credentials)
mock_iaa.assert_called_once_with(
identity_version=mock_giv.return_value)
mock_gcp.assert_called_once_with()
diff --git a/tox.ini b/tox.ini
index 44162fd..cff222d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -28,7 +28,7 @@
bash tools/pretty_tox.sh '{posargs}'
[testenv:genconfig]
-commands = oslo-config-generator --config-file etc/config-generator.tempest.conf
+commands = oslo-config-generator --config-file tempest/cmd/config-generator.tempest.conf
[testenv:cover]
setenv = OS_TEST_PATH=./tempest/tests