Merge "Move cinder tests into unversioned path - part2"
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 18269bf..2314222 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -9,6 +9,8 @@
config file which explains the purpose of each individual option. You can see
the sample config file here: :ref:`tempest-sampleconf`
+.. _tempest_cred_provider_conf:
+
Test Credentials
----------------
@@ -232,6 +234,9 @@
Enabling Remote Access to Created Servers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. _tempest_conf_network_allocation:
+
Network Creation/Usage for Servers
""""""""""""""""""""""""""""""""""
When Tempest creates servers for testing, some tests require being able to
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 896cd98..1264ecc 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -67,7 +67,8 @@
HACKING
REVIEWING
microversion_testing
- test-removal
+ test_removal
+ write_tests
-------
Plugins
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 6b30825..b3af92f 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -1,3 +1,5 @@
+.. _tempest_plugin:
+
=============================
Tempest Test Plugin Interface
=============================
diff --git a/doc/source/test-removal.rst b/doc/source/test_removal.rst
similarity index 100%
rename from doc/source/test-removal.rst
rename to doc/source/test_removal.rst
diff --git a/doc/source/write_tests.rst b/doc/source/write_tests.rst
new file mode 100644
index 0000000..768bf0f
--- /dev/null
+++ b/doc/source/write_tests.rst
@@ -0,0 +1,243 @@
+.. _tempest_test_writing:
+
+Tempest Test Writing Guide
+==========================
+
+This guide serves as a starting point for developers working on writing new
+Tempest tests. At a high level tests in Tempest are just tests that conform to
+the standard python `unit test`_ framework. But there are several aspects of
+that are unique to tempest and it's role as an integration test suite running
+against a real cloud.
+
+.. _unit test: https://docs.python.org/3.6/library/unittest.html
+
+.. note:: This guide is for writing tests in the tempest repository. While many
+ parts of this guide are also applicable to tempest plugins, not all
+ the APIs mentioned are considered stable or recommended for use in
+ plugins. Please refer to :ref:`tempest_plugin` for details about
+ writing plugins
+
+
+Adding a New TestCase
+=====================
+
+The base unit of testing in Tempest is the `TestCase`_ (also called the test
+class). Each TestCase contains test methods which are the individual tests that
+will be executed by the test runner. But, the TestCase is the smallest self
+contained unit for tests from the tempest perspective. It's also the level at
+which tempest is parallel safe. In other words, multiple TestCases can be
+executed in parallel, but individual test methods in the same TestCase can not.
+Also, all test methods within a TestCase are assumed to be executed serially. As
+such you can use the test case to store variables that are shared between
+methods.
+
+.. _TestCase: https://docs.python.org/3.6/library/unittest.html#unittest.TestCase
+
+In standard unittest the lifecycle of a TestCase can be described in the
+following phases:
+
+ #. setUpClass
+ #. setUp
+ #. Test Execution
+ #. tearDown
+ #. doCleanups
+ #. tearDownClass
+
+setUpClass
+----------
+
+The setUpClass phase is the first phase executed by the test runner and is used
+to perform any setup required for all the test methods to be executed. In
+Tempest this is a very important step and will automatically do the necessary
+setup for interacting with the configured cloud.
+
+To accomplish this you do **not** define a setUpClass function, instead there
+are a number of predefined phases to setUpClass that are used. The phases are:
+
+ * skip_checks
+ * setup_credentials
+ * setup_clients
+ * resource_setup
+
+which is executed in that order. An example of a TestCase which defines all
+of these would be::
+
+ from tempest import config
+ from tempest import test
+
+ CONF = config.CONF
+
+
+ class TestExampleCase(test.BaseTestCase):
+
+ @classmethod
+ def skip_checks(cls):
+ """This section is used to evaluate config early and skip all test
+ methods based on these checks
+ """
+ super(TestExampleCase, cls).skip_checks()
+ if not CONF.section.foo
+ cls.skip('A helpful message')
+
+ @classmethod
+ def setup_credentials(cls):
+ """This section is used to do any manual credential allocation and also
+ in the case of dynamic credentials to override the default network
+ resource creation/auto allocation
+ """
+ # This call is used to tell the credential allocator to not create any
+ # network resources for this test case. It also enables selective
+ # creation of other neutron resources. NOTE: it must go before the
+ # super call
+ cls.set_network_resources()
+ super(TestExampleCase, cls).setup_credentials()
+
+ @classmethod
+ def setup_clients(cls):
+ """This section is used to setup client aliases from the manager object
+ or to initialize any additional clients. Except in a few very
+ specific situations you should not need to use this.
+ """
+ super(TestExampleCase, cls).setup_clients()
+ cls.servers_client = cls.os.servers_client
+
+ @classmethod
+ def resource_setup(cls):
+ """This section is used to create any resources or objects which are
+ going to be used and shared by **all** test methods in the
+ TestCase. Note then anything created in this section must also be
+ destroyed in the corresponding resource_cleanup() method (which will
+ be run during tearDownClass())
+ """
+ super(TestExampleCase, cls).resource_setup()
+ cls.shared_server = cls.servers_client.create_server(...)
+
+
+Allocating Credentials
+''''''''''''''''''''''
+
+Since Tempest tests are all about testing a running cloud, every test will need
+credentials to be able to make API requests against the cloud. Since this is
+critical to operation and, when running in parallel, easy to make a mistake,
+the base TestCase class will automatically allocate a regular user for each
+TestCase during the setup_credentials() phase. During this process it will also
+initialize a client manager object using those credentials, which will be your
+entry point into interacting with the cloud. For more details on how credentials
+are allocated the :ref:`tempest_cred_provider_conf` section of the Tempest
+Configuration Guide provides more details on the operation of this.
+
+There are some cases when you need more than a single set of credentials, or
+credentials with a more specialized set of roles. To accomplish this you have
+to set a class variable ``credentials`` on the TestCase directly. For example::
+
+ from tempest import test
+
+ class TestExampleAdmin(test.BaseTestCase):
+
+ credentials = ['primary', 'admin']
+
+ @classmethod
+ def skip_checks(cls):
+ ...
+
+In this example the ``TestExampleAdmin`` TestCase will allocate 2 sets of
+credentials, one regular user and one admin user. The corresponding manager
+objects will be set as class variables cls.os and cls.os_adm respectively. You
+can also allocate a second user by putting **'alt'** in the list too. A set of
+alt credentials are the same as primary but can be used for tests cases that
+need a second user/project.
+
+You can also specify credentials with specific roles assigned. This is useful
+for cases where there are specific RBAC requirements hard coded into an API.
+The canonical example of this are swift tests which often want to test swift's
+concepts of operator and reseller_admin. An actual example from tempest on how
+to do this is::
+
+ class PublicObjectTest(base.BaseObjectTest):
+
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['operator_alt', CONF.object_storage.operator_role]]
+
+ @classmethod
+ def setup_credentials(cls):
+ super(PublicObjectTest, cls).setup_credentials()
+ ...
+
+In this case the manager objects will be set to ``cls.os_roles_operator`` and
+``cls.os_roles_operator_alt`` respectively.
+
+
+There is no limit to how many credentials you can allocate in this manner,
+however in almost every case you should **not** need more than 3 sets of
+credentials per test case.
+
+To figure out the mapping of manager objects set on the TestCase and the
+requested credentials you can reference:
+
++-------------------+---------------------+
+| Credentials Entry | Manager Variable |
++===================+=====================+
+| primary | cls.os |
++-------------------+---------------------+
+| admin | cls.os_adm |
++-------------------+---------------------+
+| alt | cls.os_alt |
++-------------------+---------------------+
+| [$label, $role] | cls.os_roles_$label |
++-------------------+---------------------+
+
+By default cls.os is available since it is allocated in the base tempest test
+class. (located in tempest/test.py) If your TestCase inherits from a different
+direct parent class (it'll still inherit from the BaseTestCase, just not
+directly) be sure to check if that class overrides allocated credentials.
+
+Dealing with Network Allocation
+'''''''''''''''''''''''''''''''
+
+When neutron is enabled and a testing requires networking this isn't normally
+automatically setup when a tenant is created. Since tempest needs isolated
+tenants to function properly it also needs to handle network allocation. By
+default the base test class will allocate a network, subnet, and router
+automatically. (this depends on the configured credential provider, for more
+details see: :ref:`tempest_conf_network_allocation`) However, there are
+situations where you do no need all of these resources allocated. (or your
+TestCase inherits from a class that overrides the default in tempest/test.py)
+There is a class level mechanism to override this allocation and specify which
+resources you need. To do this you need to call `cls.set_network_resources()`
+in the `setup_credentials()` method before the `super()`. For example::
+
+ from tempest import test
+
+
+ class TestExampleCase(test.BaseTestCase):
+
+ @classmethod
+ def setup_credentials(cls):
+ cls.set_network_resources(network=True, subnet=True, router=False)
+ super(TestExampleCase, cls).setup_credentials()
+
+There are 2 quirks with the usage here. First for the set_network_resources
+function to work properly it **must be called before super()**. This is so
+that children classes' settings are always used instead of a parent classes'.
+The other quirk here is that if you do not want to allocate any network
+resources for your test class simply call `set_network_resources()` without
+any arguments. For example::
+
+ from tempest import test
+
+
+ class TestExampleCase(test.BaseTestCase):
+
+ @classmethod
+ def setup_credentials(cls):
+ cls.set_network_resources()
+ super(TestExampleCase, cls).setup_credentials()
+
+This will not allocate any networking resources. This is because by default all
+the arguments default to False.
+
+It's also worth pointing out that it is common for base test classes for
+different services (and scenario tests) to override this setting. When
+inheriting from classes other than the base TestCase in tempest/test.py it is
+worth checking the immediate parent for what is set to determine if your
+class needs to override that setting.
diff --git a/releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml b/releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
new file mode 100644
index 0000000..01136c6
--- /dev/null
+++ b/releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Add missing API call, list all role inference rules,
+ to the roles_client library. This feature enables the
+ possibility of listing all role inference rules in the
+ system.
diff --git a/releasenotes/notes/remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml b/releasenotes/notes/remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
new file mode 100644
index 0000000..889e862
--- /dev/null
+++ b/releasenotes/notes/remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+ - |
+ The deprecated config option 'dvr_extra_resources' from network group has been removed.
+ This option was for extra resources which were provisioned to bind a router to Neutron
+ L3 agent. The extra resources need to be provisioned in Liberty release or older,
+ and are not required since Mitaka release. Current Tempest doesn't support Liberty, so
+ this option has been removed from Tempest.
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 12eb5e1..1ad153a 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -48,23 +48,25 @@
cls.fixed_network_name = None
network_kwargs = fixed_network.set_networks_kwarg(network)
cls.s1_name = data_utils.rand_name(cls.__name__ + '-instance')
- cls.s1 = cls.create_test_server(name=cls.s1_name,
- wait_until='ACTIVE',
- **network_kwargs)
+ cls.s1 = cls.create_test_server(name=cls.s1_name, **network_kwargs)
cls.s2_name = data_utils.rand_name(cls.__name__ + '-instance')
# If image_ref_alt is "" or None then we still want to boot a server
# but we rely on `testtools.skipUnless` decorator to actually skip
# the irrelevant tests.
cls.s2 = cls.create_test_server(
- name=cls.s2_name, image_id=cls.image_ref_alt or cls.image_ref,
- wait_until='ACTIVE')
+ name=cls.s2_name, image_id=cls.image_ref_alt or cls.image_ref)
cls.s3_name = data_utils.rand_name(cls.__name__ + '-instance')
cls.s3 = cls.create_test_server(name=cls.s3_name,
flavor=cls.flavor_ref_alt,
wait_until='ACTIVE')
+ waiters.wait_for_server_status(cls.client, cls.s1['id'],
+ 'ACTIVE')
+ waiters.wait_for_server_status(cls.client, cls.s2['id'],
+ 'ACTIVE')
+
@decorators.idempotent_id('05e8a8e7-9659-459a-989d-92c2f501f4ba')
@testtools.skipUnless(CONF.compute.image_ref != CONF.compute.image_ref_alt,
"Need distinct images to run this test")
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index e07d525..04be00b 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -381,3 +381,48 @@
role_assignments = self.role_assignments.list_role_assignments(
effective=True, **params)['role_assignments']
self.assertEmpty(role_assignments)
+
+ @decorators.idempotent_id('3748c316-c18f-4b08-997b-c60567bc6235')
+ def test_list_all_implied_roles(self):
+ # Create inference rule from "roles[0]" to "roles[1]"
+ self._create_implied_role(
+ self.roles[0]['id'], self.roles[1]['id'])
+
+ # Create inference rule from "roles[0]" to "roles[2]"
+ self._create_implied_role(
+ self.roles[0]['id'], self.roles[2]['id'])
+
+ # Create inference rule from "roles[2]" to "role"
+ self._create_implied_role(
+ self.roles[2]['id'], self.role['id'])
+
+ rules = self.roles_client.list_all_role_inference_rules()[
+ 'role_inferences']
+ # Sort the rules by the number of inferences, since there should be 1
+ # inference between "roles[2]" and "role" and 2 inferences for
+ # "roles[0]": between "roles[1]" and "roles[2]".
+ sorted_rules = sorted(rules, key=lambda r: len(r['implies']))
+
+ # Check that 2 sets of rules are returned.
+ self.assertEqual(2, len(sorted_rules))
+ # Check that only 1 inference rule exists between "roles[2]" and "role"
+ self.assertEqual(1, len(sorted_rules[0]['implies']))
+ # Check that 2 inference rules exist for "roles[0]": one between
+ # "roles[1]" and one between "roles[2]".
+ self.assertEqual(2, len(sorted_rules[1]['implies']))
+
+ # Check that "roles[2]" is the "prior_role" and that "role" is the
+ # "implies" role.
+ self.assertEqual(self.roles[2]['id'],
+ sorted_rules[0]['prior_role']['id'])
+ self.assertEqual(self.role['id'],
+ sorted_rules[0]['implies'][0]['id'])
+
+ # Check that "roles[0]" is the "prior_role" and that "roles[1]" and
+ # "roles[2]" are the "implies" roles.
+ self.assertEqual(self.roles[0]['id'],
+ sorted_rules[1]['prior_role']['id'])
+
+ implies_ids = [r['id'] for r in sorted_rules[1]['implies']]
+ self.assertIn(self.roles[1]['id'], implies_ids)
+ self.assertIn(self.roles[2]['id'], implies_ids)
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index e1970b9..e7460af 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -28,7 +28,6 @@
class L3AgentSchedulerTestJSON(base.BaseAdminNetworkTest):
_agent_mode = 'legacy'
- is_dvr_router = False
"""
Tests the following operations in the Neutron API using the REST client for
@@ -68,42 +67,6 @@
raise exceptions.InvalidConfiguration(msg)
cls.router = cls.create_router()
- if CONF.network.dvr_extra_resources:
- # NOTE(armax): If DVR is an available extension, and the created
- # router is indeed a distributed one, more resources need to be
- # provisioned in order to bind the router to the L3 agent in the
- # Liberty release or older, and are not required since the Mitaka
- # release.
- if test.is_extension_enabled('dvr', 'network'):
- cls.is_dvr_router = cls.admin_routers_client.show_router(
- cls.router['id'])['router'].get('distributed', False)
- if cls.is_dvr_router:
- cls.network = cls.create_network()
- cls.create_subnet(cls.network)
- cls.port = cls.create_port(cls.network)
- cls.routers_client.add_router_interface(
- cls.router['id'], port_id=cls.port['id'])
- # NOTE: Sometimes we have seen this test fail with dvr in,
- # multinode tests, since the dhcp port is not created
- # before the test gets executed and so the router is not
- # scheduled on the given agent. By adding the external
- # gateway info to the router, the router should be properly
- # scheduled in the dvr_snat node. This is a temporary work
- # around to prevent a race condition.
- external_gateway_info = {
- 'network_id': CONF.network.public_network_id,
- 'enable_snat': True}
- cls.admin_routers_client.update_router(
- cls.router['id'],
- external_gateway_info=external_gateway_info)
-
- @classmethod
- def resource_cleanup(cls):
- if cls.is_dvr_router:
- cls.routers_client.remove_router_interface(cls.router['id'],
- port_id=cls.port['id'])
- super(L3AgentSchedulerTestJSON, cls).resource_cleanup()
-
@decorators.idempotent_id('b7ce6e89-e837-4ded-9b78-9ed3c9c6a45a')
def test_list_routers_on_l3_agent(self):
self.admin_agents_client.list_routers_on_l3_agent(self.agent['id'])
diff --git a/tempest/api/volume/admin/v2/test_volume_manage.py b/tempest/api/volume/admin/test_volume_manage.py
similarity index 96%
rename from tempest/api/volume/admin/v2/test_volume_manage.py
rename to tempest/api/volume/admin/test_volume_manage.py
index f983490..a039085 100644
--- a/tempest/api/volume/admin/v2/test_volume_manage.py
+++ b/tempest/api/volume/admin/test_volume_manage.py
@@ -22,11 +22,11 @@
CONF = config.CONF
-class VolumeManageAdminV2Test(base.BaseVolumeAdminTest):
+class VolumeManageAdminTest(base.BaseVolumeAdminTest):
@classmethod
def skip_checks(cls):
- super(VolumeManageAdminV2Test, cls).skip_checks()
+ super(VolumeManageAdminTest, cls).skip_checks()
if not CONF.volume_feature_enabled.manage_volume:
raise cls.skipException("Manage volume tests are disabled")
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index aeec52a..7f291e9 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -55,3 +55,8 @@
def test_volume_force_delete_when_volume_is_error(self):
# test force delete when status of volume is error
self._create_reset_and_force_delete_temp_volume('error')
+
+ @decorators.idempotent_id('b957cabd-1486-4e21-90cf-a9ed3c39dfb2')
+ def test_volume_force_delete_when_volume_is_maintenance(self):
+ # test force delete when status of volume is maintenance
+ self._create_reset_and_force_delete_temp_volume('maintenance')
diff --git a/tempest/api/volume/v2/test_image_metadata.py b/tempest/api/volume/test_image_metadata.py
similarity index 100%
rename from tempest/api/volume/v2/test_image_metadata.py
rename to tempest/api/volume/test_image_metadata.py
diff --git a/tempest/api/volume/v3/test_versions.py b/tempest/api/volume/test_versions.py
similarity index 100%
rename from tempest/api/volume/v3/test_versions.py
rename to tempest/api/volume/test_versions.py
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 315472e..a2a3d27 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -30,7 +30,6 @@
@classmethod
def setup_clients(cls):
super(VolumesActionsTest, cls).setup_clients()
- cls.client = cls.volumes_client
if CONF.service_available.glance:
# Check if glance v1 is available to determine which client to use.
if CONF.image_feature_enabled.api_v1:
@@ -56,23 +55,23 @@
# Create a server
server = self.create_server(wait_until='ACTIVE')
# Volume is attached and detached successfully from an instance
- self.client.attach_volume(self.volume['id'],
- instance_uuid=server['id'],
- mountpoint='/dev/%s' %
- CONF.compute.volume_device_name)
- waiters.wait_for_volume_resource_status(self.client,
+ self.volumes_client.attach_volume(self.volume['id'],
+ instance_uuid=server['id'],
+ mountpoint='/dev/%s' %
+ CONF.compute.volume_device_name)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
self.volume['id'], 'in-use')
- self.client.detach_volume(self.volume['id'])
- waiters.wait_for_volume_resource_status(self.client,
+ self.volumes_client.detach_volume(self.volume['id'])
+ waiters.wait_for_volume_resource_status(self.volumes_client,
self.volume['id'], 'available')
@decorators.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
def test_volume_bootable(self):
# Verify that a volume bootable flag is retrieved
for bool_bootable in [True, False]:
- self.client.set_bootable_volume(self.volume['id'],
- bootable=bool_bootable)
- fetched_volume = self.client.show_volume(
+ self.volumes_client.set_bootable_volume(self.volume['id'],
+ bootable=bool_bootable)
+ fetched_volume = self.volumes_client.show_volume(
self.volume['id'])['volume']
# Get Volume information
# NOTE(masayukig): 'bootable' is "true" or "false" in the current
@@ -87,16 +86,18 @@
# Create a server
server = self.create_server(wait_until='ACTIVE')
# Verify that a volume's attachment information is retrieved
- self.client.attach_volume(self.volume['id'],
- instance_uuid=server['id'],
- mountpoint='/dev/%s' %
- CONF.compute.volume_device_name)
- waiters.wait_for_volume_resource_status(self.client, self.volume['id'],
+ self.volumes_client.attach_volume(self.volume['id'],
+ instance_uuid=server['id'],
+ mountpoint='/dev/%s' %
+ CONF.compute.volume_device_name)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ self.volume['id'],
'in-use')
- self.addCleanup(waiters.wait_for_volume_resource_status, self.client,
+ self.addCleanup(waiters.wait_for_volume_resource_status,
+ self.volumes_client,
self.volume['id'], 'available')
- self.addCleanup(self.client.detach_volume, self.volume['id'])
- volume = self.client.show_volume(self.volume['id'])['volume']
+ self.addCleanup(self.volumes_client.detach_volume, self.volume['id'])
+ volume = self.volumes_client.show_volume(self.volume['id'])['volume']
self.assertIn('attachments', volume)
attachment = volume['attachments'][0]
@@ -115,7 +116,7 @@
# there is no way to delete it from Cinder, so we delete it from Glance
# using the Glance image_client and from Cinder via tearDownClass.
image_name = data_utils.rand_name(self.__class__.__name__ + '-Image')
- body = self.client.upload_volume(
+ body = self.volumes_client.upload_volume(
self.volume['id'], image_name=image_name,
disk_format=CONF.volume.disk_format)['os-volume_upload_image']
image_id = body["image_id"]
@@ -123,30 +124,30 @@
self.image_client.delete_image,
image_id)
waiters.wait_for_image_status(self.image_client, image_id, 'active')
- waiters.wait_for_volume_resource_status(self.client,
+ waiters.wait_for_volume_resource_status(self.volumes_client,
self.volume['id'], 'available')
@decorators.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
def test_reserve_unreserve_volume(self):
# Mark volume as reserved.
- body = self.client.reserve_volume(self.volume['id'])
+ body = self.volumes_client.reserve_volume(self.volume['id'])
# To get the volume info
- body = self.client.show_volume(self.volume['id'])['volume']
+ body = self.volumes_client.show_volume(self.volume['id'])['volume']
self.assertIn('attaching', body['status'])
# Unmark volume as reserved.
- body = self.client.unreserve_volume(self.volume['id'])
+ body = self.volumes_client.unreserve_volume(self.volume['id'])
# To get the volume info
- body = self.client.show_volume(self.volume['id'])['volume']
+ body = self.volumes_client.show_volume(self.volume['id'])['volume']
self.assertIn('available', body['status'])
@decorators.idempotent_id('fff74e1e-5bd3-4b33-9ea9-24c103bc3f59')
def test_volume_readonly_update(self):
for readonly in [True, False]:
# Update volume readonly
- self.client.update_volume_readonly(self.volume['id'],
- readonly=readonly)
+ self.volumes_client.update_volume_readonly(self.volume['id'],
+ readonly=readonly)
# Get Volume information
- fetched_volume = self.client.show_volume(
+ fetched_volume = self.volumes_client.show_volume(
self.volume['id'])['volume']
# NOTE(masayukig): 'readonly' is "True" or "False" in the current
# cinder implementation. So we need to cast boolean values to str
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index f6fd86e..925beee 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -66,7 +66,8 @@
description = data_utils.rand_name("volume-backup-description")
backup = self.create_backup(volume_id=volume['id'],
name=backup_name,
- description=description)
+ description=description,
+ container='container')
self.assertEqual(backup_name, backup['name'])
waiters.wait_for_volume_resource_status(self.volumes_client,
volume['id'], 'available')
@@ -75,6 +76,7 @@
backup = self.backups_client.show_backup(backup['id'])['backup']
self.assertEqual(backup_name, backup['name'])
self.assertEqual(description, backup['description'])
+ self.assertEqual('container', backup['container'])
# Get all backups with detail
backups = self.backups_client.list_backups(
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 6e6cd35..2e30d04 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -68,3 +68,17 @@
self.assertRaises(lib_exc.BadRequest,
self.snapshots_client.list_snapshots,
limit='invalid')
+
+ @test.attr(type=['negative'])
+ @decorators.idempotent_id('27b5f37f-bf69-4e8c-986e-c44f3d6819b8')
+ def test_list_snapshots_invalid_param_sort(self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.snapshots_client.list_snapshots,
+ sort_key='invalid')
+
+ @test.attr(type=['negative'])
+ @decorators.idempotent_id('b68deeda-ca79-4a32-81af-5c51179e553a')
+ def test_list_snapshots_invalid_param_marker(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.snapshots_client.list_snapshots,
+ marker=data_utils.rand_uuid())
diff --git a/tempest/api/volume/v2/test_volumes_snapshots_negative.py b/tempest/api/volume/v2/test_volumes_snapshots_negative.py
deleted file mode 100644
index 42ae859..0000000
--- a/tempest/api/volume/v2/test_volumes_snapshots_negative.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright 2017 Red Hat, Inc.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.api.volume import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-from tempest import test
-
-CONF = config.CONF
-
-
-class VolumesSnapshotNegativeTest(base.BaseVolumeTest):
-
- @classmethod
- def skip_checks(cls):
- super(VolumesSnapshotNegativeTest, cls).skip_checks()
- if not CONF.volume_feature_enabled.snapshot:
- raise cls.skipException("Cinder volume snapshots are disabled")
-
- @test.attr(type=['negative'])
- @decorators.idempotent_id('27b5f37f-bf69-4e8c-986e-c44f3d6819b8')
- def test_list_snapshots_invalid_param_sort(self):
- self.assertRaises(lib_exc.BadRequest,
- self.snapshots_client.list_snapshots,
- sort_key='invalid')
-
- @test.attr(type=['negative'])
- @decorators.idempotent_id('b68deeda-ca79-4a32-81af-5c51179e553a')
- def test_list_snapshots_invalid_param_marker(self):
- self.assertRaises(lib_exc.NotFound,
- self.snapshots_client.list_snapshots,
- marker=data_utils.rand_uuid())
diff --git a/tempest/api/volume/v3/__init__.py b/tempest/api/volume/v3/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/v3/__init__.py
+++ /dev/null
diff --git a/tempest/config.py b/tempest/config.py
index 35eb187..6e8ca76 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -613,18 +613,6 @@
default=False,
help="The environment does not support network separation "
"between tenants."),
- # TODO(ylobankov): Delete this option once the Liberty release is EOL.
- cfg.BoolOpt('dvr_extra_resources',
- default=True,
- help="Whether or not to create internal network, subnet, "
- "port and add network interface to distributed router "
- "in L3 agent scheduler test. Extra resources need to be "
- "provisioned in order to bind router to L3 agent in the "
- "Liberty release or older, and are not required since "
- "the Mitaka release.",
- deprecated_for_removal=True,
- deprecated_reason='This config switch was added for Liberty '
- 'which is not supported anymore.')
]
network_feature_group = cfg.OptGroup(name='network-feature-enabled',
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
index 4c4b5eb..33a7757 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -100,8 +100,10 @@
'id': {'type': 'string'},
'links': parameter_types.links
},
- 'additionalProperties': False,
- 'required': ['id', 'links']
+ # NOTE(gmann): This will be empty object if there is no
+ # flavor info present in DB. This can happen when flavor info is
+ # deleted after server creation.
+ 'additionalProperties': False
},
'fault': {
'type': 'object',
diff --git a/tempest/lib/services/identity/v3/roles_client.py b/tempest/lib/services/identity/v3/roles_client.py
index 0df23ce..43e3c01 100644
--- a/tempest/lib/services/identity/v3/roles_client.py
+++ b/tempest/lib/services/identity/v3/roles_client.py
@@ -214,6 +214,18 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
+ def list_all_role_inference_rules(self):
+ """Lists all role inference rules.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#list-all-role-inference-rules
+ """
+ resp, body = self.get('role_inferences')
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
def check_role_inference_rule(self, prior_role, implies_role):
"""Check a role inference rule."""
resp, body = self.head('roles/%s/implies/%s' %
diff --git a/tempest/lib/services/volume/v2/snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
index 2bdf1b1..983ed89 100644
--- a/tempest/lib/services/volume/v2/snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -27,7 +27,8 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-with-details
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots
"""
url = 'snapshots'
if detail:
@@ -45,7 +46,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-details
"""
url = "snapshots/%s" % snapshot_id
resp, body = self.get(url)
@@ -71,7 +72,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot
"""
put_body = json.dumps({'snapshot': kwargs})
resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
@@ -84,7 +85,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot
"""
resp, body = self.delete("snapshots/%s" % snapshot_id)
self.expected_success(202, resp.status)
@@ -136,7 +137,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata
"""
url = "snapshots/%s/metadata" % snapshot_id
resp, body = self.get(url)
@@ -149,7 +150,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata
"""
put_body = json.dumps(kwargs)
url = "snapshots/%s/metadata" % snapshot_id
diff --git a/tempest/lib/services/volume/v2/types_client.py b/tempest/lib/services/volume/v2/types_client.py
index 31597d7..5d30615 100644
--- a/tempest/lib/services/volume/v2/types_client.py
+++ b/tempest/lib/services/volume/v2/types_client.py
@@ -41,7 +41,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-types-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-types
"""
url = 'types'
if params:
@@ -57,7 +57,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-details
"""
url = "types/%s" % volume_type_id
resp, body = self.get(url)
@@ -70,7 +70,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type
"""
post_body = json.dumps({'volume_type': kwargs})
resp, body = self.post('types', post_body)
@@ -83,7 +83,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type
"""
resp, body = self.delete("types/%s" % volume_type_id)
self.expected_success(202, resp.status)
@@ -138,7 +138,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type
"""
put_body = json.dumps({'volume_type': kwargs})
resp, body = self.put('types/%s' % volume_type_id, put_body)
@@ -156,7 +156,7 @@
updated value.
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-extra-specs-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-extra-specs-for-a-volume-type
"""
url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
put_body = json.dumps(extra_specs)
@@ -170,7 +170,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#add-type-access-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#add-private-volume-type-access
"""
post_body = json.dumps({'addProjectAccess': kwargs})
url = 'types/%s/action' % volume_type_id
@@ -183,7 +183,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#remove-type-access-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#remove-private-volume-type-access
"""
post_body = json.dumps({'removeProjectAccess': kwargs})
url = 'types/%s/action' % volume_type_id
@@ -196,7 +196,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#list-type-access-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-private-volume-type-access-details
"""
url = 'types/%s/os-volume-type-access' % volume_type_id
resp, body = self.get(url)
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index f59abb7..72823c0 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -106,7 +106,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume
+ http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-to-server
"""
post_body = json.dumps({'os-attach': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -163,7 +163,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume
+ http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-size
"""
post_body = json.dumps({'os-extend': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -176,7 +176,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-status
+ http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-statuses
"""
post_body = json.dumps({'os-reset_status': kwargs})
resp, body = self.post('volumes/%s/action' % volume_id, post_body)
@@ -307,7 +307,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-image-metadata
+ http://developer.openstack.org/api-ref/block-storage/v2/#set-image-metadata-for-volume
"""
post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
url = "volumes/%s/action" % (volume_id)
@@ -344,7 +344,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#show_backend_capabilities
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-back-end-capabilities
"""
url = 'capabilities/%s' % host
resp, body = self.get(url)
diff --git a/tempest/tests/lib/services/identity/v3/test_roles_client.py b/tempest/tests/lib/services/identity/v3/test_roles_client.py
index 41cea85..8d6bb42 100644
--- a/tempest/tests/lib/services/identity/v3/test_roles_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_roles_client.py
@@ -26,6 +26,18 @@
FAKE_ROLE_ID_2 = "2"
FAKE_ROLE_NAME_2 = "test2"
+ FAKE_ROLE_ID_3 = "3"
+ FAKE_ROLE_NAME_3 = "test3"
+
+ FAKE_ROLE_ID_4 = "4"
+ FAKE_ROLE_NAME_4 = "test4"
+
+ FAKE_ROLE_ID_5 = "5"
+ FAKE_ROLE_NAME_5 = "test5"
+
+ FAKE_ROLE_ID_6 = "6"
+ FAKE_ROLE_NAME_6 = "test6"
+
FAKE_ROLE_INFO = {
"role": {
"domain_id": FAKE_DOMAIN_ID,
@@ -77,8 +89,8 @@
}
}
- FAKE_LIST_ROLE_INFERENCES_RULES = {
- "role_inference": {
+ COMMON_FAKE_LIST_ROLE_INFERENCE_RULES = [
+ {
"prior_role": {
"id": FAKE_ROLE_ID,
"name": FAKE_ROLE_NAME,
@@ -97,20 +109,60 @@
}
},
{
- "id": "3",
- "name": "test3",
+ "id": FAKE_ROLE_ID_3,
+ "name": FAKE_ROLE_NAME_3,
"links": {
- "self": "http://example.com/identity/v3/roles/3"
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_3)
}
}
]
},
+ {
+ "prior_role": {
+ "id": FAKE_ROLE_ID_4,
+ "name": FAKE_ROLE_NAME_4,
+ "links": {
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_4)
+ }
+ },
+ "implies": [
+ {
+ "id": FAKE_ROLE_ID_5,
+ "name": FAKE_ROLE_NAME_5,
+ "links": {
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_5)
+ }
+ },
+ {
+ "id": FAKE_ROLE_ID_6,
+ "name": FAKE_ROLE_NAME_6,
+ "links": {
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_6)
+ }
+ }
+ ]
+ }
+ ]
+
+ FAKE_LIST_ROLE_INFERENCE_RULES = {
+ "role_inference": COMMON_FAKE_LIST_ROLE_INFERENCE_RULES[0],
"links": {
"self": "http://example.com/identity/v3/roles/"
"%s/implies" % FAKE_ROLE_ID
}
}
+ FAKE_LIST_ALL_ROLE_INFERENCE_RULES = {
+ "role_inferences": COMMON_FAKE_LIST_ROLE_INFERENCE_RULES,
+ "links": {
+ "self": "http://example.com/identity/v3/role_inferences"
+ }
+ }
+
def setUp(self):
super(TestRolesClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -254,10 +306,17 @@
self.check_service_client_function(
self.client.list_role_inferences_rules,
'tempest.lib.common.rest_client.RestClient.get',
- self.FAKE_LIST_ROLE_INFERENCES_RULES,
+ self.FAKE_LIST_ROLE_INFERENCE_RULES,
bytes_body,
prior_role=self.FAKE_ROLE_ID)
+ def _test_list_all_role_inference_rules(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_all_role_inference_rules,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ALL_ROLE_INFERENCE_RULES,
+ bytes_body)
+
def test_create_role_with_str_body(self):
self._test_create_role()
@@ -441,3 +500,9 @@
status=204,
prior_role=self.FAKE_ROLE_ID,
implies_role=self.FAKE_ROLE_ID_2)
+
+ def test_list_all_role_inference_rules_with_str_body(self):
+ self._test_list_all_role_inference_rules()
+
+ def test_list_all_role_inference_rules_with_bytes_body(self):
+ self._test_list_all_role_inference_rules(bytes_body=True)
diff --git a/tempest/tests/lib/services/test_clients.py b/tempest/tests/lib/services/test_clients.py
index a3b390e..a837199 100644
--- a/tempest/tests/lib/services/test_clients.py
+++ b/tempest/tests/lib/services/test_clients.py
@@ -100,9 +100,8 @@
def test___init___no_module(self):
auth_provider = fake_auth_provider.FakeAuthProvider()
class_names = ['FakeServiceClient1', 'FakeServiceClient2']
- with testtools.ExpectedException(ImportError, '.*fake_module.*'):
- clients.ClientsFactory('fake_module', class_names,
- auth_provider)
+ self.assertRaises(ImportError, clients.ClientsFactory,
+ 'fake_module', class_names, auth_provider)
def test___init___not_a_class(self):
class_names = ['FakeServiceClient1', 'FakeServiceClient2']
diff --git a/tox.ini b/tox.ini
index b3052eb..892f834 100644
--- a/tox.ini
+++ b/tox.ini
@@ -36,7 +36,6 @@
commands = oslo-config-generator --config-file tempest/cmd/config-generator.tempest.conf
[testenv:cover]
-setenv = OS_TEST_PATH=./tempest/tests
commands = python setup.py testr --coverage --testr-arg='tempest\.tests {posargs}'
[testenv:all]