Merge "Use os.path.join as possible"
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 2a3edf4..59a2f64 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -24,6 +24,7 @@
import os
import subprocess
+import sys
# Build the plugin registry
def build_plugin_registry(app):
@@ -31,16 +32,20 @@
os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
subprocess.call(['tools/generate-tempest-plugins-list.sh'], cwd=root_dir)
+def autodoc_skip_member_handler(app, what, name, obj, skip, options):
+ return skip or (what == "class" and not name.startswith("test"))
+
def setup(app):
+ app.connect('autodoc-skip-member', autodoc_skip_member_handler)
if os.getenv('GENERATE_TEMPEST_PLUGIN_LIST', 'true').lower() == 'true':
app.connect('builder-inited', build_plugin_registry)
-
-
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
+sys.path.insert(0, os.path.abspath('../../tempest'))
+sys.path.insert(0, os.path.abspath('../../tempest/api'))
# -- General configuration -----------------------------------------------------
@@ -204,3 +209,10 @@
('index', 'doc-tempest.tex', u'Tempest Testing Project',
u'OpenStack Foundation', 'manual'),
]
+
+latex_use_xindy = False
+
+latex_elements = {
+ 'maxlistdepth': 20,
+ 'printindex': '\\footnotesize\\raggedright\\printindex'
+}
diff --git a/doc/source/index.rst b/doc/source/index.rst
index d4dc166..66e68ea 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -56,6 +56,13 @@
supported_version
+Description of Tests
+--------------------
+.. toctree::
+ :maxdepth: 2
+
+ tests/modules
+
For Contributors
================
diff --git a/doc/source/tests/modules.rst b/doc/source/tests/modules.rst
new file mode 100644
index 0000000..026a7a5
--- /dev/null
+++ b/doc/source/tests/modules.rst
@@ -0,0 +1,21 @@
+Description of Tests
+====================
+
+OpenStack Services Integration Tests
+------------------------------------
+.. toctree::
+ :maxdepth: 2
+
+ scenario/modules
+
+OpenStack Services API Tests
+----------------------------
+.. toctree::
+ :maxdepth: 2
+
+ compute/modules
+ identity/modules
+ image/modules
+ network/modules
+ object_storage/modules
+ volume/modules
diff --git a/releasenotes/notes/xenapi_apis-conf-fcca549283e53ed6.yaml b/releasenotes/notes/xenapi_apis-conf-fcca549283e53ed6.yaml
new file mode 100644
index 0000000..4d26210
--- /dev/null
+++ b/releasenotes/notes/xenapi_apis-conf-fcca549283e53ed6.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+ - |
+ A number of Compute APIs that only worked with the XenAPI virt driver have
+ been removed in the Compute service. As a result, their corresponding tests
+ are now disabled by default. They can be re-enabled using the new
+ ``[compute_feature_enabled] xenapi_apis`` config option.
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 0901374..4cc5fdd 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -13,12 +13,22 @@
# under the License.
from tempest.api.compute import base
+from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
+CONF = config.CONF
+
+# TODO(stephenfin): Remove these tests once the nova Ussuri branch goes EOL
class AgentsAdminTestJSON(base.BaseV2ComputeAdminTest):
- """Tests Agents API"""
+ """Tests Compute Agents API"""
+
+ @classmethod
+ def skip_checks(cls):
+ super(AgentsAdminTestJSON, cls).skip_checks()
+ if not CONF.compute_feature_enabled.xenapi_apis:
+ raise cls.skipException('The os-agents API is not supported.')
@classmethod
def setup_clients(cls):
@@ -46,7 +56,7 @@
@decorators.idempotent_id('1fc6bdc8-0b6d-4cc7-9f30-9b04fabe5b90')
def test_create_agent(self):
- # Create an agent.
+ """Test creating a compute agent"""
params = self._param_helper(
hypervisor='kvm', os='win', architecture='x86',
version='7.0', url='xxx://xxxx/xxx/xxx',
@@ -58,6 +68,7 @@
@decorators.idempotent_id('dc9ffd51-1c50-4f0e-a820-ae6d2a568a9e')
def test_update_agent(self):
+ """Test updating a compute agent"""
# Create and update an agent.
body = self.client.create_agent(**self.params_agent)['agent']
self.addCleanup(self.client.delete_agent, body['agent_id'])
@@ -71,7 +82,7 @@
@decorators.idempotent_id('470e0b89-386f-407b-91fd-819737d0b335')
def test_delete_agent(self):
- # Create an agent and delete it.
+ """Test deleting a compute agent"""
body = self.client.create_agent(**self.params_agent)['agent']
self.client.delete_agent(body['agent_id'])
@@ -82,7 +93,7 @@
@decorators.idempotent_id('6a326c69-654b-438a-80a3-34bcc454e138')
def test_list_agents(self):
- # Create an agent and list all agents.
+ """Test listing compute agents"""
body = self.client.create_agent(**self.params_agent)['agent']
self.addCleanup(self.client.delete_agent, body['agent_id'])
agents = self.client.list_agents()['agents']
@@ -91,7 +102,7 @@
@decorators.idempotent_id('eabadde4-3cd7-4ec4-a4b5-5a936d2d4408')
def test_list_agents_with_filter(self):
- # Create agents and list the agent builds by the filter.
+ """Test listing compute agents by the filter"""
body = self.client.create_agent(**self.params_agent)['agent']
self.addCleanup(self.client.delete_agent, body['agent_id'])
params = self._param_helper(
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 7a3bfdf..2716259 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -71,10 +71,11 @@
class AggregatesAdminTestJSON(AggregatesAdminTestBase):
+ """Tests Aggregates API that require admin privileges"""
@decorators.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
def test_aggregate_create_delete(self):
- # Create and delete an aggregate.
+ """Test create/delete aggregate"""
aggregate = self._create_test_aggregate()
self.assertIsNone(aggregate['availability_zone'])
@@ -83,7 +84,7 @@
@decorators.idempotent_id('5873a6f8-671a-43ff-8838-7ce430bb6d0b')
def test_aggregate_create_delete_with_az(self):
- # Create and delete an aggregate.
+ """Test create/delete aggregate with availability_zone"""
az_name = data_utils.rand_name(self.az_name_prefix)
aggregate = self._create_test_aggregate(availability_zone=az_name)
self.assertEqual(az_name, aggregate['availability_zone'])
@@ -93,7 +94,7 @@
@decorators.idempotent_id('68089c38-04b1-4758-bdf0-cf0daec4defd')
def test_aggregate_create_verify_entry_in_list(self):
- # Create an aggregate and ensure it is listed.
+ """Test listing aggregate should contain the created aggregate"""
aggregate = self._create_test_aggregate()
aggregates = self.client.list_aggregates()['aggregates']
self.assertIn((aggregate['id'], aggregate['availability_zone']),
@@ -102,7 +103,7 @@
@decorators.idempotent_id('36ec92ca-7a73-43bc-b920-7531809e8540')
def test_aggregate_create_update_metadata_get_details(self):
- # Create an aggregate and ensure its details are returned.
+ """Test set/get aggregate metadata"""
aggregate = self._create_test_aggregate()
body = self.client.show_aggregate(aggregate['id'])['aggregate']
self.assertEqual(aggregate['name'], body['name'])
@@ -121,7 +122,7 @@
@decorators.idempotent_id('4d2b2004-40fa-40a1-aab2-66f4dab81beb')
def test_aggregate_create_update_with_az(self):
- # Update an aggregate and ensure properties are updated correctly
+ """Test create/update aggregate with availability_zone"""
aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
az_name = data_utils.rand_name(self.az_name_prefix)
aggregate = self._create_test_aggregate(
@@ -148,7 +149,7 @@
@decorators.idempotent_id('c8e85064-e79b-4906-9931-c11c24294d02')
def test_aggregate_add_remove_host(self):
- # Add a host to the given aggregate and remove.
+ """Test adding host to and removing host from aggregate"""
self.useFixture(fixtures.LockFixture('availability_zone'))
aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
aggregate = self._create_test_aggregate(name=aggregate_name)
@@ -169,7 +170,10 @@
@decorators.idempotent_id('7f6a1cc5-2446-4cdb-9baa-b6ae0a919b72')
def test_aggregate_add_host_list(self):
- # Add a host to the given aggregate and list.
+ """Test listing aggregate contains the host added to the aggregate
+
+ Add a host to the given aggregate and list.
+ """
self.useFixture(fixtures.LockFixture('availability_zone'))
aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
aggregate = self._create_test_aggregate(name=aggregate_name)
@@ -188,7 +192,10 @@
@decorators.idempotent_id('eeef473c-7c52-494d-9f09-2ed7fc8fc036')
def test_aggregate_add_host_get_details(self):
- # Add a host to the given aggregate and get details.
+ """Test showing aggregate contains the host added to the aggregate
+
+ Add a host to the given aggregate and get details.
+ """
self.useFixture(fixtures.LockFixture('availability_zone'))
aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
aggregate = self._create_test_aggregate(name=aggregate_name)
@@ -204,7 +211,7 @@
@decorators.idempotent_id('96be03c7-570d-409c-90f8-e4db3c646996')
def test_aggregate_add_host_create_server_with_az(self):
- # Add a host to the given aggregate and create a server.
+ """Test adding a host to the given aggregate and creating a server"""
self.useFixture(fixtures.LockFixture('availability_zone'))
az_name = data_utils.rand_name(self.az_name_prefix)
aggregate = self._create_test_aggregate(availability_zone=az_name)
@@ -233,6 +240,11 @@
class AggregatesAdminTestV241(AggregatesAdminTestBase):
+ """Tests Aggregates API that require admin privileges
+
+ Tests Aggregates API that require admin privileges with compute
+ microversion greater than 2.40.
+ """
min_microversion = '2.41'
# NOTE(gmann): This test tests the Aggregate APIs response schema
@@ -241,6 +253,11 @@
@decorators.idempotent_id('fdf24d9e-8afa-4700-b6aa-9c498351504f')
def test_create_update_show_aggregate_add_remove_host(self):
+ """Test response schema of aggregates API
+
+ Test response schema of aggregates API(create/update/show/add host/
+ remove host) with compute microversion greater than 2.40.
+ """
# Update and add a host to the given aggregate and get details.
self.useFixture(fixtures.LockFixture('availability_zone'))
# Checking create aggregate API response schema
diff --git a/tempest/api/compute/admin/test_availability_zone.py b/tempest/api/compute/admin/test_availability_zone.py
index bbd39b6..3eb0d9a 100644
--- a/tempest/api/compute/admin/test_availability_zone.py
+++ b/tempest/api/compute/admin/test_availability_zone.py
@@ -27,12 +27,12 @@
@decorators.idempotent_id('d3431479-8a09-4f76-aa2d-26dc580cb27c')
def test_get_availability_zone_list(self):
- # List of availability zone
+ """Test listing availability zones"""
availability_zone = self.client.list_availability_zones()
self.assertNotEmpty(availability_zone['availabilityZoneInfo'])
@decorators.idempotent_id('ef726c58-530f-44c2-968c-c7bed22d5b8c')
def test_get_availability_zone_list_detail(self):
- # List of availability zones and available services
+ """Test listing availability zones with detail"""
availability_zone = self.client.list_availability_zones(detail=True)
self.assertNotEmpty(availability_zone['availabilityZoneInfo'])
diff --git a/tempest/api/compute/admin/test_availability_zone_negative.py b/tempest/api/compute/admin/test_availability_zone_negative.py
index a58c22c..6e576e8 100644
--- a/tempest/api/compute/admin/test_availability_zone_negative.py
+++ b/tempest/api/compute/admin/test_availability_zone_negative.py
@@ -18,7 +18,7 @@
class AZAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
- """Tests Availability Zone API List"""
+ """Negative Tests of Availability Zone API List"""
@classmethod
def setup_clients(cls):
@@ -28,8 +28,12 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('bf34dca2-fdc3-4073-9c02-7648d9eae0d7')
def test_get_availability_zone_list_detail_with_non_admin_user(self):
- # List of availability zones and available services with
- # non-administrator user
+ """Test listing availability zone with detail by non-admin user
+
+ List of availability zones and available services with
+ non-administrator user is not allowed.
+ """
+
self.assertRaises(
lib_exc.Forbidden,
self.non_adm_client.list_availability_zones, detail=True)
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index 66c2c2d..9de3da9 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -22,6 +22,7 @@
class FixedIPsTestJson(base.BaseV2ComputeAdminTest):
+ """Test fixed ips API"""
@classmethod
def skip_checks(cls):
@@ -56,13 +57,16 @@
@decorators.idempotent_id('16b7d848-2f7c-4709-85a3-2dfb4576cc52')
def test_list_fixed_ip_details(self):
+ """Test getting fixed ip details"""
fixed_ip = self.client.show_fixed_ip(self.ip)
self.assertEqual(fixed_ip['fixed_ip']['address'], self.ip)
@decorators.idempotent_id('5485077b-7e46-4cec-b402-91dc3173433b')
def test_set_reserve(self):
+ """Test reserving fixed ip"""
self.client.reserve_fixed_ip(self.ip, reserve="None")
@decorators.idempotent_id('7476e322-b9ff-4710-bf82-49d51bac6e2e')
def test_set_unreserve(self):
+ """Test unreserving fixed ip"""
self.client.reserve_fixed_ip(self.ip, unreserve="None")
diff --git a/tempest/api/compute/admin/test_fixed_ips_negative.py b/tempest/api/compute/admin/test_fixed_ips_negative.py
index 7d41f46..1629faa 100644
--- a/tempest/api/compute/admin/test_fixed_ips_negative.py
+++ b/tempest/api/compute/admin/test_fixed_ips_negative.py
@@ -22,6 +22,7 @@
class FixedIPsNegativeTestJson(base.BaseV2ComputeAdminTest):
+ """Negative tests of fixed ips API"""
@classmethod
def skip_checks(cls):
@@ -58,12 +59,14 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('9f17f47d-daad-4adc-986e-12370c93e407')
def test_list_fixed_ip_details_with_non_admin_user(self):
+ """Test listing fixed ip with detail by non-admin user is forbidden"""
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.show_fixed_ip, self.ip)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('ce60042c-fa60-4836-8d43-1c8e3359dc47')
def test_set_reserve_with_non_admin_user(self):
+ """Test reserving fixed ip by non-admin user is forbidden"""
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.reserve_fixed_ip,
self.ip, reserve="None")
@@ -71,6 +74,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('f1f7a35b-0390-48c5-9803-5f27461439db')
def test_set_unreserve_with_non_admin_user(self):
+ """Test unreserving fixed ip by non-admin user is forbidden"""
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.reserve_fixed_ip,
self.ip, unreserve="None")
@@ -78,6 +82,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('f51cf464-7fc5-4352-bc3e-e75cfa2cb717')
def test_set_reserve_with_invalid_ip(self):
+ """Test reserving invalid fixed ip should fail"""
# NOTE(maurosr): since this exercises the same code snippet, we do it
# only for reserve action
# NOTE(eliqiao): in Juno, the exception is NotFound, but in master, we
@@ -90,6 +95,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('fd26ef50-f135-4232-9d32-281aab3f9176')
def test_fixed_ip_with_invalid_action(self):
+ """Test operating fixed ip with invalid action should fail"""
self.assertRaises(lib_exc.BadRequest,
self.client.reserve_fixed_ip,
self.ip, invalid_action="None")
diff --git a/tempest/api/compute/admin/test_flavors_access.py b/tempest/api/compute/admin/test_flavors_access.py
index b8e2b42..87ab7c7 100644
--- a/tempest/api/compute/admin/test_flavors_access.py
+++ b/tempest/api/compute/admin/test_flavors_access.py
@@ -43,8 +43,12 @@
@decorators.idempotent_id('ea2c2211-29fa-4db9-97c3-906d36fad3e0')
def test_flavor_access_list_with_private_flavor(self):
- # Test to make sure that list flavor access on a newly created
- # private flavor will return an empty access list
+ """Test listing flavor access for a private flavor
+
+ Listing flavor access on a newly created private flavor will return
+ an empty access list.
+ """
+ # Test to make sure that
flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
disk=self.disk, is_public='False')
@@ -54,7 +58,7 @@
@decorators.idempotent_id('59e622f6-bdf6-45e3-8ba8-fedad905a6b4')
def test_flavor_access_add_remove(self):
- # Test to add and remove flavor access to a given tenant.
+ """Test add/remove flavor access to a given project"""
flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
disk=self.disk, is_public='False')
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 45ca10a..ac09cb0 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -46,7 +46,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0621c53e-d45d-40e7-951d-43e5e257b272')
def test_flavor_access_list_with_public_flavor(self):
- # Test to list flavor access with exceptions by querying public flavor
+ """Test listing flavor access of a public flavor should fail"""
flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
disk=self.disk, is_public='True')
self.assertRaises(lib_exc.NotFound,
@@ -56,7 +56,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('41eaaade-6d37-4f28-9c74-f21b46ca67bd')
def test_flavor_non_admin_add(self):
- # Test to add flavor access as a user without admin privileges.
+ """Test adding flavor access by a non-admin user is forbidden"""
flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
disk=self.disk, is_public='False')
self.assertRaises(lib_exc.Forbidden,
@@ -67,7 +67,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('073e79a6-c311-4525-82dc-6083d919cb3a')
def test_flavor_non_admin_remove(self):
- # Test to remove flavor access as a user without admin privileges.
+ """Test removing flavor access by a non-admin user should fail"""
flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
disk=self.disk, is_public='False')
@@ -84,6 +84,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('f3592cc0-0306-483c-b210-9a7b5346eddc')
def test_add_flavor_access_duplicate(self):
+ """Test adding duplicate flavor access to same flavor should fail"""
# Create a new flavor.
flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
disk=self.disk, is_public='False')
@@ -104,6 +105,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('1f710927-3bc7-4381-9f82-0ca6e42644b7')
def test_remove_flavor_access_not_found(self):
+ """Test removing non existent flavor access should fail"""
# Create a new flavor.
flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
disk=self.disk, is_public='False')
diff --git a/tempest/api/compute/admin/test_flavors_microversions.py b/tempest/api/compute/admin/test_flavors_microversions.py
index 31b9217..d904cbd 100644
--- a/tempest/api/compute/admin/test_flavors_microversions.py
+++ b/tempest/api/compute/admin/test_flavors_microversions.py
@@ -18,6 +18,8 @@
class FlavorsV255TestJSON(base.BaseV2ComputeAdminTest):
+ """Test flavors API with compute microversion greater than 2.54"""
+
min_microversion = '2.55'
max_microversion = 'latest'
@@ -26,6 +28,11 @@
@decorators.idempotent_id('61976b25-488d-41dc-9dcb-cb9693a7b075')
def test_crud_flavor(self):
+ """Test create/show/update/list flavor
+
+ Check the response schema of flavors API with microversion greater
+ than 2.54.
+ """
flavor_id = data_utils.rand_int_id(start=1000)
# Checking create API response schema
new_flavor_id = self.create_flavor(ram=512,
@@ -44,6 +51,7 @@
class FlavorsV261TestJSON(FlavorsV255TestJSON):
+ """Test flavors API with compute microversion greater than 2.60"""
min_microversion = '2.61'
max_microversion = 'latest'
diff --git a/tempest/api/compute/admin/test_live_migration_negative.py b/tempest/api/compute/admin/test_live_migration_negative.py
index 8327a3b..80c0525 100644
--- a/tempest/api/compute/admin/test_live_migration_negative.py
+++ b/tempest/api/compute/admin/test_live_migration_negative.py
@@ -24,6 +24,8 @@
class LiveMigrationNegativeTest(base.BaseV2ComputeAdminTest):
+ """Negative tests of live migration"""
+
@classmethod
def skip_checks(cls):
super(LiveMigrationNegativeTest, cls).skip_checks()
@@ -40,7 +42,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('7fb7856e-ae92-44c9-861a-af62d7830bcb')
def test_invalid_host_for_migration(self):
- # Migrating to an invalid host should not change the status
+ """Test migrating to an invalid host should not change the status"""
target_host = data_utils.rand_name('host')
server = self.create_test_server(wait_until="ACTIVE")
@@ -52,6 +54,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('6e2f94f5-2ee8-4830-bef5-5bc95bb0795b')
def test_live_block_migration_suspended(self):
+ """Test migrating a suspended server should not change the status"""
server = self.create_test_server(wait_until="ACTIVE")
self.admin_servers_client.suspend_server(server['id'])
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 0060ffe..9d5e0c9 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -97,9 +97,11 @@
class QuotasAdminTestJSON(QuotasAdminTestBase):
+ """Test compute quotas by admin user"""
+
@decorators.idempotent_id('3b0a7c8f-cf58-46b8-a60c-715a32a8ba7d')
def test_get_default_quotas(self):
- # Admin can get the default resource quota set for a tenant
+ """Test admin can get the default compute quota set for a project"""
expected_quota_set = self.default_quota_set | set(['id'])
quota_set = self.adm_client.show_default_quota_set(
self.demo_tenant_id)['quota_set']
@@ -109,7 +111,7 @@
@decorators.idempotent_id('55fbe2bf-21a9-435b-bbd2-4162b0ed799a')
def test_update_all_quota_resources_for_tenant(self):
- # Admin can update all the resource quota limits for a tenant
+ """Test admin can update all the compute quota limits for a project"""
default_quota_set = self.adm_client.show_default_quota_set(
self.demo_tenant_id)['quota_set']
new_quota_set = {'metadata_items': 256, 'ram': 10240,
@@ -140,11 +142,12 @@
# TODO(afazekas): merge these test cases
@decorators.idempotent_id('ce9e0815-8091-4abd-8345-7fe5b85faa1d')
def test_get_updated_quotas(self):
+ """Test that GET shows the updated quota set of project"""
self._get_updated_quotas()
@decorators.idempotent_id('389d04f0-3a41-405f-9317-e5f86e3c44f0')
def test_delete_quota(self):
- # Admin can delete the resource quota set for a project
+ """Test admin can delete the compute quota set for a project"""
project_name = data_utils.rand_name('ram_quota_project')
project_desc = project_name + '-desc'
project = identity.identity_utils(self.os_admin).create_project(
@@ -165,26 +168,40 @@
class QuotasAdminTestV236(QuotasAdminTestBase):
- min_microversion = '2.36'
+ """Test compute quotas with microversion greater than 2.35
+
# NOTE(gmann): This test tests the Quota APIs response schema
# for 2.36 microversion. No specific assert or behaviour verification
# is needed.
+ """
+
+ min_microversion = '2.36'
@decorators.idempotent_id('4268b5c9-92e5-4adc-acf1-3a2798f3d803')
def test_get_updated_quotas(self):
- # Checking Quota update, get, get details APIs response schema
+ """Test compute quotas API with microversion greater than 2.35
+
+ Checking compute quota update, get, get details APIs response schema.
+ """
self._get_updated_quotas()
class QuotasAdminTestV257(QuotasAdminTestBase):
- min_microversion = '2.57'
+ """Test compute quotas with microversion greater than 2.56
+
# NOTE(gmann): This test tests the Quota APIs response schema
# for 2.57 microversion. No specific assert or behaviour verification
# is needed.
+ """
+
+ min_microversion = '2.57'
@decorators.idempotent_id('e641e6c6-e86c-41a4-9e5c-9493c0ae47ad')
def test_get_updated_quotas(self):
- # Checking Quota update, get, get details APIs response schema
+ """Test compute quotas API with microversion greater than 2.56
+
+ Checking compute quota update, get, get details APIs response schema.
+ """
self._get_updated_quotas()
@@ -212,6 +229,7 @@
# 'danger' flag.
@decorators.idempotent_id('7932ab0f-5136-4075-b201-c0e2338df51a')
def test_update_default_quotas(self):
+ """Test updating default compute quota class set"""
# get the current 'default' quota class values
body = (self.adm_client.show_quota_class_set('default')
['quota_class_set'])
diff --git a/tempest/api/compute/admin/test_server_diagnostics_negative.py b/tempest/api/compute/admin/test_server_diagnostics_negative.py
index 6215c37..8f14cbc 100644
--- a/tempest/api/compute/admin/test_server_diagnostics_negative.py
+++ b/tempest/api/compute/admin/test_server_diagnostics_negative.py
@@ -18,6 +18,7 @@
class ServerDiagnosticsNegativeTest(base.BaseV2ComputeAdminTest):
+ """Negative tests of server diagnostics"""
@classmethod
def setup_clients(cls):
@@ -27,7 +28,10 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('e84e2234-60d2-42fa-8b30-e2d3049724ac')
def test_get_server_diagnostics_by_non_admin(self):
- # Non-admin user cannot view server diagnostics according to policy
+ """Test getting server diagnostics by non-admin user is forbidden
+
+ Non-admin user cannot view server diagnostics according to policy.
+ """
server_id = self.create_test_server(wait_until='ACTIVE')['id']
self.assertRaises(lib_exc.Forbidden,
self.client.show_server_diagnostics, server_id)
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 170b2cc..ab1b49a 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -14,10 +14,13 @@
from tempest.api.compute import base
from tempest.common import waiters
+from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
+CONF = config.CONF
+
class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
"""Tests Servers API using admin privileges"""
@@ -45,7 +48,7 @@
@decorators.idempotent_id('06f960bb-15bb-48dc-873d-f96e89be7870')
def test_list_servers_filter_by_error_status(self):
- # Filter the list of servers by server error status
+ """Test filtering the list of servers by server error status"""
params = {'status': 'error'}
self.client.reset_state(self.s1_id, state='error')
body = self.non_admin_client.list_servers(**params)
@@ -61,6 +64,7 @@
@decorators.idempotent_id('d56e9540-73ed-45e0-9b88-98fc419087eb')
def test_list_servers_detailed_filter_by_invalid_status(self):
+ """Test filtering the list of servers by invalid server status"""
params = {'status': 'invalid_status'}
if self.is_requested_microversion_compatible('2.37'):
body = self.client.list_servers(detail=True, **params)
@@ -72,8 +76,11 @@
@decorators.idempotent_id('51717b38-bdc1-458b-b636-1cf82d99f62f')
def test_list_servers_by_admin(self):
- # Listing servers by admin user returns a list which doesn't
- # contain the other tenants' server by default
+ """Test listing servers by admin without other projects
+
+ Listing servers by admin user returns a list which doesn't
+ contain the other projects' server by default.
+ """
body = self.client.list_servers(detail=True)
servers = body['servers']
@@ -85,8 +92,11 @@
@decorators.idempotent_id('9f5579ae-19b4-4985-a091-2a5d56106580')
def test_list_servers_by_admin_with_all_tenants(self):
- # Listing servers by admin user with all tenants parameter
- # Here should be listed all servers
+ """Test listing servers by admin with all tenants
+
+ Listing servers by admin user with all tenants parameter,
+ all servers should be listed.
+ """
params = {'all_tenants': ''}
body = self.client.list_servers(detail=True, **params)
servers = body['servers']
@@ -98,8 +108,10 @@
@decorators.related_bug('1659811')
@decorators.idempotent_id('7e5d6b8f-454a-4ba1-8ae2-da857af8338b')
def test_list_servers_by_admin_with_specified_tenant(self):
- # In nova v2, tenant_id is ignored unless all_tenants is specified
+ """Test listing servers by admin with specified project
+ In nova v2, tenant_id is ignored unless all_tenants is specified.
+ """
# List the primary tenant but get nothing due to odd specified behavior
tenant_id = self.non_admin_client.tenant_id
params = {'tenant_id': tenant_id}
@@ -128,7 +140,7 @@
@decorators.idempotent_id('86c7a8f7-50cf-43a9-9bac-5b985317134f')
def test_list_servers_filter_by_exist_host(self):
- # Filter the list of servers by existent host
+ """Test filtering the list of servers by existent host"""
server = self.client.show_server(self.s1_id)['server']
hostname = server['OS-EXT-SRV-ATTR:host']
params = {'host': hostname, 'all_tenants': '1'}
@@ -144,6 +156,7 @@
@decorators.idempotent_id('ee8ae470-db70-474d-b752-690b7892cab1')
def test_reset_state_server(self):
+ """Test resetting server state to error/active"""
# Reset server's state to 'error'
self.client.reset_state(self.s1_id, state='error')
@@ -160,9 +173,11 @@
@decorators.idempotent_id('682cb127-e5bb-4f53-87ce-cb9003604442')
def test_rebuild_server_in_error_state(self):
- # The server in error state should be rebuilt using the provided
- # image and changed to ACTIVE state
+ """Test rebuilding server in error state
+ The server in error state should be rebuilt using the provided
+ image and changed to ACTIVE state.
+ """
# resetting vm state require admin privilege
self.client.reset_state(self.s1_id, state='error')
rebuilt_server = self.non_admin_client.rebuild_server(
@@ -188,6 +203,11 @@
@decorators.idempotent_id('7a1323b4-a6a2-497a-96cb-76c07b945c71')
def test_reset_network_inject_network_info(self):
+ """Test resetting and injecting network info of a server"""
+ if not CONF.compute_feature_enabled.xenapi_apis:
+ raise self.skipException(
+ 'The resetNetwork server action is not supported.')
+
# Reset Network of a Server
server = self.create_test_server(wait_until='ACTIVE')
self.client.reset_network(server['id'])
@@ -196,6 +216,7 @@
@decorators.idempotent_id('fdcd9b33-0903-4e00-a1f7-b5f6543068d6')
def test_create_server_with_scheduling_hint(self):
+ """Test creating server with scheduling hint"""
# Create a server with scheduler hints.
hints = {
'same_host': self.s1_id
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index f720b84..f52d4c0 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -26,7 +26,7 @@
class ServersAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
- """Tests Servers API using admin privileges"""
+ """Negative Tests of Servers API using admin privileges"""
@classmethod
def setup_clients(cls):
@@ -47,6 +47,7 @@
'Resize not available.')
@decorators.attr(type=['negative'])
def test_resize_server_using_overlimit_ram(self):
+ """Test resizing server using over limit ram should fail"""
# NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
self.useFixture(fixtures.LockFixture('compute_quotas'))
quota_set = self.quotas_client.show_quota_set(
@@ -69,6 +70,7 @@
'Resize not available.')
@decorators.attr(type=['negative'])
def test_resize_server_using_overlimit_vcpus(self):
+ """Test resizing server using over limit vcpus should fail"""
# NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
self.useFixture(fixtures.LockFixture('compute_quotas'))
quota_set = self.quotas_client.show_quota_set(
@@ -89,6 +91,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('b0b4d8af-1256-41ef-9ee7-25f1c19dde80')
def test_reset_state_server_invalid_state(self):
+ """Test resetting server state to invalid state value should fail"""
self.assertRaises(lib_exc.BadRequest,
self.client.reset_state, self.s1_id,
state='invalid')
@@ -96,6 +99,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('4cdcc984-fab0-4577-9a9d-6d558527ee9d')
def test_reset_state_server_invalid_type(self):
+ """Test resetting server state to invalid state type should fail"""
self.assertRaises(lib_exc.BadRequest,
self.client.reset_state, self.s1_id,
state=1)
@@ -103,13 +107,14 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('e741298b-8df2-46f0-81cb-8f814ff2504c')
def test_reset_state_server_nonexistent_server(self):
+ """Test resetting a non existent server's state should fail"""
self.assertRaises(lib_exc.NotFound,
self.client.reset_state, '999', state='error')
@decorators.attr(type=['negative'])
@decorators.idempotent_id('46a4e1ca-87ae-4d28-987a-1b6b136a0221')
def test_migrate_non_existent_server(self):
- # migrate a non existent server
+ """Test migrating a non existent server should fail"""
self.assertRaises(lib_exc.NotFound,
self.client.migrate_server,
data_utils.rand_uuid())
@@ -121,6 +126,7 @@
'Suspend is not available.')
@decorators.attr(type=['negative'])
def test_migrate_server_invalid_state(self):
+ """Test migrating a server with invalid state should fail"""
# create server.
server = self.create_test_server(wait_until='ACTIVE')
server_id = server['id']
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index d4c60b3..c24f420 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -26,6 +26,7 @@
class TenantUsagesTestJSON(base.BaseV2ComputeAdminTest):
+ """Test tenant usages"""
@classmethod
def setup_clients(cls):
@@ -67,7 +68,7 @@
@decorators.idempotent_id('062c8ae9-9912-4249-8b51-e38d664e926e')
def test_list_usage_all_tenants(self):
- # Get usage for all tenants
+ """Test getting usage for all tenants"""
tenant_usage = self.call_until_valid(
self.adm_client.list_tenant_usages, VALID_WAIT,
start=self.start, end=self.end, detailed="1")['tenant_usages'][0]
@@ -75,7 +76,7 @@
@decorators.idempotent_id('94135049-a4c5-4934-ad39-08fa7da4f22e')
def test_get_usage_tenant(self):
- # Get usage for a specific tenant
+ """Test getting usage for a specific tenant"""
tenant_usage = self.call_until_valid(
self.adm_client.show_tenant_usage, VALID_WAIT,
self.tenant_id, start=self.start, end=self.end)['tenant_usage']
@@ -84,7 +85,7 @@
@decorators.idempotent_id('9d00a412-b40e-4fd9-8eba-97b496316116')
def test_get_usage_tenant_with_non_admin_user(self):
- # Get usage for a specific tenant with non admin user
+ """Test getting usage for a specific tenant with non admin user"""
tenant_usage = self.call_until_valid(
self.client.show_tenant_usage, VALID_WAIT,
self.tenant_id, start=self.start, end=self.end)['tenant_usage']
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
index cb60b8d..4b5a5d5 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
@@ -21,6 +21,7 @@
class TenantUsagesNegativeTestJSON(base.BaseV2ComputeAdminTest):
+ """Negative tests of compute tenant usages API"""
@classmethod
def setup_clients(cls):
@@ -43,7 +44,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('8b21e135-d94b-4991-b6e9-87059609c8ed')
def test_get_usage_tenant_with_empty_tenant_id(self):
- # Get usage for a specific tenant empty
+ """Test getting tenant usage with empty tenant id should fail"""
params = {'start': self.start,
'end': self.end}
self.assertRaises(lib_exc.NotFound,
@@ -53,7 +54,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('4079dd2a-9e8d-479f-869d-6fa985ce45b6')
def test_get_usage_tenant_with_invalid_date(self):
- # Get usage for tenant with invalid date
+ """Test getting tenant usage with invalid time range should fail"""
params = {'start': self.end,
'end': self.start}
self.assertRaises(lib_exc.BadRequest,
@@ -63,7 +64,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('bbe6fe2c-15d8-404c-a0a2-44fad0ad5cc7')
def test_list_usage_all_tenants_with_non_admin_user(self):
- # Get usage for all tenants with non admin user
+ """Test listing usage of all tenants by non-admin user is forbidden"""
params = {'start': self.start,
'end': self.end,
'detailed': "1"}
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 74570ce..8b847fc 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -566,17 +566,19 @@
# the state of the volume to change to available. This is so we don't
# error out when trying to delete the volume during teardown.
if volume['multiattach']:
+ att = waiters.wait_for_volume_attachment_create(
+ self.volumes_client, volume['id'], server['id'])
self.addCleanup(waiters.wait_for_volume_attachment_remove,
self.volumes_client, volume['id'],
- attachment['id'])
+ att['attachment_id'])
else:
self.addCleanup(waiters.wait_for_volume_resource_status,
self.volumes_client, volume['id'], 'available')
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ volume['id'], 'in-use')
# Ignore 404s on detach in case the server is deleted or the volume
# is already detached.
self.addCleanup(self._detach_volume, server, volume)
- waiters.wait_for_volume_resource_status(self.volumes_client,
- volume['id'], 'in-use')
return attachment
def create_volume_snapshot(self, volume_id, name=None, description=None,
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index 58861a1..9ab75c5 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -28,6 +28,9 @@
flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
flavor_min_detail = {'id': flavor['id'], 'links': flavor['links'],
'name': flavor['name']}
+ # description field is added to the response of list_flavors in 2.55
+ if not self.is_requested_microversion_compatible('2.54'):
+ flavor_min_detail.update({'description': flavor['description']})
self.assertIn(flavor_min_detail, flavors)
@decorators.idempotent_id('6e85fde4-b3cd-4137-ab72-ed5f418e8c24')
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 2adc482..6097bbc 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -25,13 +25,13 @@
class FloatingIPsTestJSON(base.BaseFloatingIPsTest):
+ """Test floating ips API with compute microversion less than 2.36"""
max_microversion = '2.35'
@decorators.idempotent_id('f7bfb946-297e-41b8-9e8c-aba8e9bb5194')
def test_allocate_floating_ip(self):
- # Positive test:Allocation of a new floating IP to a project
- # should be successful
+ """Test allocating a floating ip to a project"""
body = self.client.create_floating_ip(
pool=CONF.network.floating_network_name)['floating_ip']
floating_ip_id_allocated = body['id']
@@ -45,8 +45,7 @@
@decorators.idempotent_id('de45e989-b5ca-4a9b-916b-04a52e7bbb8b')
def test_delete_floating_ip(self):
- # Positive test:Deletion of valid floating IP from project
- # should be successful
+ """Test deleting a valid floating ip from project"""
# Creating the floating IP that is to be deleted in this method
floating_ip_body = self.client.create_floating_ip(
pool=CONF.network.floating_network_name)['floating_ip']
@@ -59,6 +58,7 @@
class FloatingIPsAssociationTestJSON(base.BaseFloatingIPsTest):
+ """Test floating ips association with microversion less than 2.44"""
max_microversion = '2.43'
@@ -80,9 +80,7 @@
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
def test_associate_disassociate_floating_ip(self):
- # Positive test:Associate and disassociate the provided floating IP
- # to a specific server should be successful
-
+ """Test associate/disassociate floating ip to a server"""
# Association of floating IP to fixed IP address
self.client.associate_floating_ip_to_server(
self.floating_ip,
@@ -102,6 +100,12 @@
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
def test_associate_already_associated_floating_ip(self):
+ """Test associating an already associated floating ip
+
+ First associate a floating ip to server1, then associate the floating
+ ip to server2, the floating ip will be associated to server2 and no
+ longer associated to server1.
+ """
# positive test:Association of an already associated floating IP
# to specific server should change the association of the Floating IP
# Create server so as to use for Multiple association
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index 9257458..e99e218 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -25,6 +25,7 @@
class FloatingIPsNegativeTestJSON(base.BaseFloatingIPsTest):
+ """Test floating ips API with compute microversion less than 2.36"""
max_microversion = '2.35'
@@ -46,8 +47,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('6e0f059b-e4dd-48fb-8207-06e3bba5b074')
def test_allocate_floating_ip_from_nonexistent_pool(self):
- # Negative test:Allocation of a new floating IP from a nonexistent_pool
- # to a project should fail
+ """Test allocating floating ip from non existent pool should fail"""
self.assertRaises(lib_exc.NotFound,
self.client.create_floating_ip,
pool="non_exist_pool")
@@ -55,15 +55,14 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('ae1c55a8-552b-44d4-bfb6-2a115a15d0ba')
def test_delete_nonexistent_floating_ip(self):
- # Negative test:Deletion of a nonexistent floating IP
- # from project should fail
-
+ """Test deleting non existent floating ip should fail"""
# Deleting the non existent floating IP
self.assertRaises(lib_exc.NotFound, self.client.delete_floating_ip,
self.non_exist_id)
class FloatingIPsAssociationNegativeTestJSON(base.BaseFloatingIPsTest):
+ """Test floating ips API with compute microversion less than 2.44"""
max_microversion = '2.43'
@@ -76,8 +75,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('595fa616-1a71-4670-9614-46564ac49a4c')
def test_associate_nonexistent_floating_ip(self):
- # Negative test:Association of a non existent floating IP
- # to specific server should fail
+ """Test associating non existent floating ip to server should fail"""
# Associating non existent floating IP
self.assertRaises(lib_exc.NotFound,
self.client.associate_floating_ip_to_server,
@@ -86,7 +84,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0a081a66-e568-4e6b-aa62-9587a876dca8')
def test_dissociate_nonexistent_floating_ip(self):
- # Negative test:Dissociation of a non existent floating IP should fail
+ """Test dissociating non existent floating ip should fail"""
# Dissociating non existent floating IP
self.assertRaises(lib_exc.NotFound,
self.client.disassociate_floating_ip_from_server,
@@ -95,7 +93,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('804b4fcb-bbf5-412f-925d-896672b61eb3')
def test_associate_ip_to_server_without_passing_floating_ip(self):
- # Negative test:Association of empty floating IP to specific server
+ """Test associating empty floating ip to server should fail"""
# should raise NotFound or BadRequest(In case of Nova V2.1) exception.
self.assertRaises((lib_exc.NotFound, lib_exc.BadRequest),
self.client.associate_floating_ip_to_server,
@@ -106,10 +104,13 @@
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
def test_associate_ip_to_server_with_floating_ip(self):
- # The VM have one port
- # Associate floating IP A to the VM
- # Associate floating IP B which is from same pool with floating IP A
- # to the VM, should raise BadRequest exception
+ """Test associating floating ip to server already with floating ip
+
+ 1. The VM have one port
+ 2. Associate floating IP A to the VM
+ 3. Associate floating IP B which is from same pool with floating IP A
+ to the VM, should raise BadRequest exception
+ """
body = self.client.create_floating_ip(
pool=CONF.network.public_network_id)['floating_ip']
self.addCleanup(self.client.delete_floating_ip, body['id'])
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index 944f798..6bfee95 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -21,6 +21,7 @@
class FloatingIPDetailsTestJSON(base.BaseFloatingIPsTest):
+ """Test floating ip details with compute microversion less than 2.36"""
max_microversion = '2.35'
@@ -37,7 +38,7 @@
@decorators.idempotent_id('16db31c3-fb85-40c9-bbe2-8cf7b67ff99f')
def test_list_floating_ips(self):
- # Positive test:Should return the list of floating IPs
+ """Test listing floating ips"""
body = self.client.list_floating_ips()['floating_ips']
floating_ips = body
self.assertNotEmpty(floating_ips,
@@ -47,7 +48,7 @@
@decorators.idempotent_id('eef497e0-8ff7-43c8-85ef-558440574f84')
def test_get_floating_ip_details(self):
- # Positive test:Should be able to GET the details of floatingIP
+ """Test getting floating ip details"""
# Creating a floating IP for which details are to be checked
body = self.client.create_floating_ip(
pool=CONF.network.floating_network_name)['floating_ip']
@@ -68,7 +69,7 @@
@decorators.idempotent_id('df389fc8-56f5-43cc-b290-20eda39854d3')
def test_list_floating_ip_pools(self):
- # Positive test:Should return the list of floating IP Pools
+ """Test listing floating ip pools"""
floating_ip_pools = self.pools_client.list_floating_ip_pools()
self.assertNotEmpty(floating_ip_pools['floating_ip_pools'],
"Expected floating IP Pools. Got zero.")
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
index d69248c..aa0320d 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
@@ -23,14 +23,18 @@
class FloatingIPDetailsNegativeTestJSON(base.BaseFloatingIPsTest):
+ """Negative tests of floating ip detail
+
+ Negative tests of floating ip detail with compute microversion less
+ than 2.36.
+ """
max_microversion = '2.35'
@decorators.attr(type=['negative'])
@decorators.idempotent_id('7ab18834-4a4b-4f28-a2c5-440579866695')
def test_get_nonexistent_floating_ip_details(self):
- # Negative test:Should not be able to GET the details
- # of non-existent floating IP
+ """Test getting non existent floating ip should fail"""
# Creating a non-existent floatingIP id
if CONF.service_available.neutron:
non_exist_id = data_utils.rand_uuid()
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 1f3af5f..561265f 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -28,6 +28,8 @@
class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
+ """Test image metadata with compute microversion less than 2.39"""
+
max_microversion = '2.38'
@classmethod
@@ -89,7 +91,10 @@
@decorators.idempotent_id('37ec6edd-cf30-4c53-bd45-ae74db6b0531')
def test_list_image_metadata(self):
- # All metadata key/value pairs for an image should be returned
+ """Test listing image metadata
+
+ All metadata key/value pairs for an image should be returned.
+ """
resp_metadata = self.client.list_image_metadata(self.image_id)
expected = {'metadata': {
'os_version': 'value1', 'os_distro': 'value2'}}
@@ -97,7 +102,10 @@
@decorators.idempotent_id('ece7befc-d3ce-42a4-b4be-c3067a418c29')
def test_set_image_metadata(self):
- # The metadata for the image should match the new values
+ """Test setting image metadata
+
+ The metadata for the image should match the new values.
+ """
req_metadata = {'os_version': 'value2', 'architecture': 'value3'}
self.client.set_image_metadata(self.image_id,
req_metadata)
@@ -108,7 +116,10 @@
@decorators.idempotent_id('7b491c11-a9d5-40fe-a696-7f7e03d3fea2')
def test_update_image_metadata(self):
- # The metadata for the image should match the updated values
+ """Test updating image medata
+
+ The metadata for the image should match the updated values.
+ """
req_metadata = {'os_version': 'alt1', 'architecture': 'value3'}
self.client.update_image_metadata(self.image_id,
req_metadata)
@@ -122,15 +133,21 @@
@decorators.idempotent_id('4f5db52f-6685-4c75-b848-f4bb363f9aa6')
def test_get_image_metadata_item(self):
- # The value for a specific metadata key should be returned
+ """Test getting image metadata item
+
+ The value for a specific metadata key should be returned.
+ """
meta = self.client.show_image_metadata_item(self.image_id,
'os_distro')['meta']
self.assertEqual('value2', meta['os_distro'])
@decorators.idempotent_id('f2de776a-4778-4d90-a5da-aae63aee64ae')
def test_set_image_metadata_item(self):
- # The value provided for the given meta item should be set for
- # the image
+ """Test setting image metadata item
+
+ The value provided for the given meta item should be set for
+ the image.
+ """
meta = {'os_version': 'alt'}
self.client.set_image_metadata_item(self.image_id,
'os_version', meta)
@@ -140,7 +157,10 @@
@decorators.idempotent_id('a013796c-ba37-4bb5-8602-d944511def14')
def test_delete_image_metadata_item(self):
- # The metadata value/key pair should be deleted from the image
+ """Test deleting image metadata item
+
+ The metadata value/key pair should be deleted from the image.
+ """
self.client.delete_image_metadata_item(self.image_id,
'os_version')
resp_metadata = self.client.list_image_metadata(self.image_id)
diff --git a/tempest/api/compute/images/test_image_metadata_negative.py b/tempest/api/compute/images/test_image_metadata_negative.py
index 407fb08..b9806c7 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -20,6 +20,11 @@
class ImagesMetadataNegativeTestJSON(base.BaseV2ComputeTest):
+ """Negative tests of image metadata
+
+ Negative tests of image metadata with compute microversion less than 2.39.
+ """
+
max_microversion = '2.38'
@classmethod
@@ -30,15 +35,14 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('94069db2-792f-4fa8-8bd3-2271a6e0c095')
def test_list_nonexistent_image_metadata(self):
- # Negative test: List on nonexistent image
- # metadata should not happen
+ """Test listing metadata of a non existence image should fail"""
self.assertRaises(lib_exc.NotFound, self.client.list_image_metadata,
data_utils.rand_uuid())
@decorators.attr(type=['negative'])
@decorators.idempotent_id('a403ef9e-9f95-427c-b70a-3ce3388796f1')
def test_update_nonexistent_image_metadata(self):
- # Negative test:An update should not happen for a non-existent image
+ """Test updating metadata of a non existence image should fail"""
meta = {'os_distro': 'alt1', 'os_version': 'alt2'}
self.assertRaises(lib_exc.NotFound,
self.client.update_image_metadata,
@@ -47,7 +51,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('41ae052c-6ee6-405c-985e-5712393a620d')
def test_get_nonexistent_image_metadata_item(self):
- # Negative test: Get on non-existent image should not happen
+ """Test getting metadata of a non existence image should fail"""
self.assertRaises(lib_exc.NotFound,
self.client.show_image_metadata_item,
data_utils.rand_uuid(), 'os_version')
@@ -55,7 +59,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('dc64f2ce-77e8-45b0-88c8-e15041d08eaf')
def test_set_nonexistent_image_metadata(self):
- # Negative test: Metadata should not be set to a non-existent image
+ """Test setting metadata of a non existence image should fail"""
meta = {'os_distro': 'alt1', 'os_version': 'alt2'}
self.assertRaises(lib_exc.NotFound, self.client.set_image_metadata,
data_utils.rand_uuid(), meta)
@@ -63,8 +67,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('2154fd03-ab54-457c-8874-e6e3eb56e9cf')
def test_set_nonexistent_image_metadata_item(self):
- # Negative test: Metadata item should not be set to a
- # nonexistent image
+ """Test setting metadata item of a non existence image should fail"""
meta = {'os_distro': 'alt'}
self.assertRaises(lib_exc.NotFound,
self.client.set_image_metadata_item,
@@ -74,8 +77,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('848e157f-6bcf-4b2e-a5dd-5124025a8518')
def test_delete_nonexistent_image_metadata_item(self):
- # Negative test: Shouldn't be able to delete metadata
- # item from non-existent image
+ """Test deleting metadata item of a non existence image should fail"""
self.assertRaises(lib_exc.NotFound,
self.client.delete_image_metadata_item,
data_utils.rand_uuid(), 'os_distro')
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index c1af6c7..0601bbe 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -99,6 +99,7 @@
class AttachInterfacesTestJSON(AttachInterfacesTestBase):
+ """Test attaching interfaces"""
def wait_for_port_detach(self, port_id):
"""Waits for the port's device_id to be unset.
@@ -230,6 +231,7 @@
@decorators.idempotent_id('73fe8f02-590d-4bf1-b184-e9ca81065051')
@utils.services('network')
def test_create_list_show_delete_interfaces_by_network_port(self):
+ """Test create/list/show/delete interfaces by network port"""
server, ifs, _ = self._create_server_get_interfaces()
interface_count = len(ifs)
self.assertGreater(interface_count, 0)
@@ -262,6 +264,7 @@
@decorators.idempotent_id('d290c06c-f5b3-11e7-8ec8-002293781009')
@utils.services('network')
def test_create_list_show_delete_interfaces_by_fixed_ip(self):
+ """Test create/list/show/delete interfaces by fixed ip"""
# NOTE(zhufl) By default only project that is admin or network owner
# or project with role advsvc is authorised to create interfaces with
# fixed-ip, so if we don't create network for each project, do not
@@ -290,7 +293,7 @@
@decorators.idempotent_id('2f3a0127-95c7-4977-92d2-bc5aec602fb4')
def test_reassign_port_between_servers(self):
- """Tests the following:
+ """Tests reassigning port between servers
1. Create a port in Neutron.
2. Create two servers in Nova.
@@ -343,12 +346,15 @@
class AttachInterfacesUnderV243Test(AttachInterfacesTestBase):
+ """Test attaching interfaces with compute microversion less than 2.44"""
+
max_microversion = '2.43'
@decorators.attr(type='smoke')
@decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
@utils.services('network')
def test_add_remove_fixed_ip(self):
+ """Test adding and removing fixed ip from server"""
# NOTE(zhufl) By default only project that is admin or network owner
# or project with role advsvc is authorised to add interfaces with
# fixed-ip, so if we don't create network for each project, do not
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 8879369..a7e2187 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -103,6 +103,7 @@
class TaggedBootDevicesTest(DeviceTaggingBase):
+ """Test tagged boot device with compute microversion equals 2.32"""
min_microversion = '2.32'
# NOTE(mriedem): max_version looks odd but it's actually correct. Due to a
@@ -149,6 +150,16 @@
@decorators.idempotent_id('a2e65a6c-66f1-4442-aaa8-498c31778d96')
@utils.services('network', 'volume', 'image')
def test_tagged_boot_devices(self):
+ """Test tagged boot devices
+
+ 1. Create volumes
+ 2. Create networks
+ 3. Create subnets
+ 4. Create ports
+ 5. Create server, specifying tags for items in networks and
+ block_device_mapping_v2,
+ 6. Verify tagged devices are in server via metadata service
+ """
# Create volumes
# The create_volume methods waits for the volumes to be available and
# the base class will clean them up on tearDown.
@@ -300,11 +311,14 @@
class TaggedBootDevicesTest_v242(TaggedBootDevicesTest):
+ """Test tagged boot devices with compute microversion greater than 2.41"""
+
min_microversion = '2.42'
max_microversion = 'latest'
class TaggedAttachmentsTest(DeviceTaggingBase):
+ """Test tagged attachments with compute microversion greater than 2.48"""
min_microversion = '2.49'
max_microversion = 'latest'
@@ -342,6 +356,16 @@
@decorators.idempotent_id('3e41c782-2a89-4922-a9d2-9a188c4e7c7c')
@utils.services('network', 'volume', 'image')
def test_tagged_attachment(self):
+ """Test tagged attachment
+
+ 1. Create network
+ 2. Create subnet
+ 3. Create volume
+ 4. Create server
+ 5. Attach tagged networks and volume
+ 6. Verify tagged devices are in server via metadata service
+ 7. Detach tagged networks and volume
+ """
# Create network
net = self.networks_client.create_network(
name=data_utils.rand_name(
diff --git a/tempest/api/compute/servers/test_instance_actions.py b/tempest/api/compute/servers/test_instance_actions.py
index 00837eb..5ab592a 100644
--- a/tempest/api/compute/servers/test_instance_actions.py
+++ b/tempest/api/compute/servers/test_instance_actions.py
@@ -19,6 +19,8 @@
class InstanceActionsTestJSON(base.BaseV2ComputeTest):
+ """Test instance actions API"""
+
create_default_network = True
@classmethod
@@ -34,7 +36,7 @@
@decorators.idempotent_id('77ca5cc5-9990-45e0-ab98-1de8fead201a')
def test_list_instance_actions(self):
- # List actions of the provided server
+ """Test listing actions of the provided server"""
self.client.reboot_server(self.server['id'], type='HARD')
waiters.wait_for_server_status(self.client,
self.server['id'], 'ACTIVE')
@@ -47,7 +49,7 @@
@decorators.idempotent_id('aacc71ca-1d70-4aa5-bbf6-0ff71470e43c')
def test_get_instance_action(self):
- # Get the action details of the provided server
+ """Test getting the action details of the provided server"""
body = self.client.show_instance_action(
self.server['id'], self.request_id)['instanceAction']
self.assertEqual(self.server['id'], body['instance_uuid'])
@@ -55,6 +57,8 @@
class InstanceActionsV221TestJSON(base.BaseV2ComputeTest):
+ """Test instance actions with compute microversion greater than 2.20"""
+
create_default_network = True
min_microversion = '2.21'
@@ -67,8 +71,11 @@
@decorators.idempotent_id('0a0f85d4-10fa-41f6-bf80-a54fb4aa2ae1')
def test_get_list_deleted_instance_actions(self):
+ """Test listing actions for deleted instance
- # List actions of the deleted server
+ Listing actions for deleted instance should succeed and the returned
+ actions should contain 'create' and 'delete'.
+ """
server = self.create_test_server(wait_until='ACTIVE')
self.client.delete_server(server['id'])
waiters.wait_for_server_termination(self.client, server['id'])
diff --git a/tempest/api/compute/servers/test_instance_actions_negative.py b/tempest/api/compute/servers/test_instance_actions_negative.py
index 4b5a2c3..dd2bf06 100644
--- a/tempest/api/compute/servers/test_instance_actions_negative.py
+++ b/tempest/api/compute/servers/test_instance_actions_negative.py
@@ -20,6 +20,8 @@
class InstanceActionsNegativeTestJSON(base.BaseV2ComputeTest):
+ """Negative tests of instance actions"""
+
create_default_network = True
@classmethod
@@ -35,7 +37,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('67e1fce6-7ec2-45c6-92d4-0a8f1a632910')
def test_list_instance_actions_non_existent_server(self):
- # List actions of the non-existent server id
+ """Test listing actions for non existent instance should fail"""
non_existent_server_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
self.client.list_instance_actions,
@@ -44,6 +46,6 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0269f40a-6f18-456c-b336-c03623c897f1')
def test_get_instance_action_invalid_request(self):
- # Get the action details of the provided server with invalid request
+ """Test getting instance action with invalid request_id should fail"""
self.assertRaises(lib_exc.NotFound, self.client.show_instance_action,
self.server['id'], '999')
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index b95db5c..3d55696 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -20,6 +20,8 @@
class ListServersNegativeTestJSON(base.BaseV2ComputeTest):
+ """Negative tests of listing servers"""
+
create_default_network = True
@classmethod
@@ -45,7 +47,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('24a26f1a-1ddc-4eea-b0d7-a90cc874ad8f')
def test_list_servers_with_a_deleted_server(self):
- # Verify deleted servers do not show by default in list servers
+ """Test that deleted servers do not show by default in list servers"""
# List servers and verify server not returned
body = self.client.list_servers()
servers = body['servers']
@@ -56,7 +58,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('ff01387d-c7ad-47b4-ae9e-64fa214638fe')
def test_list_servers_by_non_existing_image(self):
- # Listing servers for a non existing image returns empty list
+ """Test listing servers for a non existing image returns empty list"""
body = self.client.list_servers(image='non_existing_image')
servers = body['servers']
self.assertEmpty(servers)
@@ -64,7 +66,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('5913660b-223b-44d4-a651-a0fbfd44ca75')
def test_list_servers_by_non_existing_flavor(self):
- # Listing servers by non existing flavor returns empty list
+ """Test listing servers by non existing flavor returns empty list"""
body = self.client.list_servers(flavor='non_existing_flavor')
servers = body['servers']
self.assertEmpty(servers)
@@ -72,7 +74,12 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('e2c77c4a-000a-4af3-a0bd-629a328bde7c')
def test_list_servers_by_non_existing_server_name(self):
- # Listing servers for a non existent server name returns empty list
+ """Test listing servers for a non existent server name
+
+ Listing servers for a non existent server name should return empty
+ list.
+ """
+
body = self.client.list_servers(name='non_existing_server_name')
servers = body['servers']
self.assertEmpty(servers)
@@ -80,9 +87,13 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('fcdf192d-0f74-4d89-911f-1ec002b822c4')
def test_list_servers_status_non_existing(self):
- # When invalid status is specified, up to microversion 2.37,
- # an empty list is returned, and starting from microversion 2.38,
- # a 400 error is returned in that case.
+ """Test listing servers with non existing status
+
+ When invalid status is specified, up to microversion 2.37,
+ an empty list is returned, and starting from microversion 2.38,
+ a 400 error is returned in that case.
+ """
+
if self.is_requested_microversion_compatible('2.37'):
body = self.client.list_servers(status='non_existing_status')
servers = body['servers']
@@ -94,6 +105,12 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('d47c17fb-eebd-4287-8e95-f20a7e627b18')
def test_list_servers_by_limits_greater_than_actual_count(self):
+ """Test listing servers by limit greater than actual count
+
+ Listing servers by limit greater than actual count should return
+ all servers.
+ """
+
# 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
@@ -104,21 +121,21 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('679bc053-5e70-4514-9800-3dfab1a380a6')
def test_list_servers_by_limits_pass_string(self):
- # Return an error if a string value is passed for limit
+ """Test listing servers by non-integer limit should fail"""
self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
limit='testing')
@decorators.attr(type=['negative'])
@decorators.idempotent_id('62610dd9-4713-4ee0-8beb-fd2c1aa7f950')
def test_list_servers_by_limits_pass_negative_value(self):
- # Return an error if a negative value for limit is passed
+ """Test listing servers by negative limit should fail"""
self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
limit=-1)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('87d12517-e20a-4c9c-97b6-dd1628d6d6c9')
def test_list_servers_by_changes_since_invalid_date(self):
- # Return an error when invalid date format is passed
+ """Test listing servers by invalid changes-since format should fail"""
params = {'changes-since': '2011/01/01'}
self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
**params)
@@ -126,7 +143,12 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('74745ad8-b346-45b5-b9b8-509d7447fc1f')
def test_list_servers_by_changes_since_future_date(self):
- # Return an empty list when a date in the future is passed.
+ """Test listing servers by a future changes-since date
+
+ Return an empty list when a date in the future is passed as
+ changes-since value.
+ """
+
# updated_at field may haven't been set at the point in the boot
# process where build_request still exists, so add
# {'status': 'ACTIVE'} along with changes-since as filter.
@@ -138,7 +160,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('93055106-2d34-46fe-af68-d9ddbf7ee570')
def test_list_servers_detail_server_is_deleted(self):
- # Server details are not listed for a deleted server
+ """Test listing servers detail should not contain deleted server"""
body = self.client.list_servers(detail=True)
servers = body['servers']
actual = [srv for srv in servers
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index dcadace..10c76bb 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -19,11 +19,15 @@
class MultipleCreateTestJSON(base.BaseV2ComputeTest):
+ """Test creating multiple servers in one request"""
create_default_network = True
@decorators.idempotent_id('61e03386-89c3-449c-9bb1-a06f423fd9d1')
def test_multiple_create(self):
- # Creating server with min_count=2, 2 servers will be created.
+ """Test creating multiple servers in one request
+
+ Creating server with min_count=2, 2 servers will be created.
+ """
tenant_network = self.get_tenant_network()
body, servers = compute.create_test_server(
self.os_primary,
@@ -40,8 +44,12 @@
@decorators.idempotent_id('864777fb-2f1e-44e3-b5b9-3eb6fa84f2f7')
def test_multiple_create_with_reservation_return(self):
- # Creating multiple servers with return_reservation_id=True,
- # reservation_id will be returned.
+ """Test creating multiple servers with return_reservation_id=True
+
+ Creating multiple servers with return_reservation_id=True,
+ reservation_id will be returned.
+ """
+
body = self.create_test_server(wait_until='ACTIVE',
min_count=1,
max_count=2,
diff --git a/tempest/api/compute/servers/test_multiple_create_negative.py b/tempest/api/compute/servers/test_multiple_create_negative.py
index 6bdf83b..3a970dd 100644
--- a/tempest/api/compute/servers/test_multiple_create_negative.py
+++ b/tempest/api/compute/servers/test_multiple_create_negative.py
@@ -19,11 +19,12 @@
class MultipleCreateNegativeTestJSON(base.BaseV2ComputeTest):
+ """Negative tests of creating multiple servers in one request"""
@decorators.attr(type=['negative'])
@decorators.idempotent_id('daf29d8d-e928-4a01-9a8c-b129603f3fc0')
def test_min_count_less_than_one(self):
- # Creating server with min_count=0 should fail.
+ """Test creating server with min_count=0 should fail"""
invalid_min_count = 0
self.assertRaises(lib_exc.BadRequest, self.create_test_server,
min_count=invalid_min_count)
@@ -31,7 +32,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('999aa722-d624-4423-b813-0d1ac9884d7a')
def test_min_count_non_integer(self):
- # Creating server with non-integer min_count should fail.
+ """Test creating server with non-integer min_count should fail"""
invalid_min_count = 2.5
self.assertRaises(lib_exc.BadRequest, self.create_test_server,
min_count=invalid_min_count)
@@ -39,7 +40,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('a6f9c2ab-e060-4b82-b23c-4532cb9390ff')
def test_max_count_less_than_one(self):
- # Creating server with max_count < 1 shoudld fail.
+ """Test creating server with max_count < 1 shoudld fail"""
invalid_max_count = 0
self.assertRaises(lib_exc.BadRequest, self.create_test_server,
max_count=invalid_max_count)
@@ -47,7 +48,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('9c5698d1-d7af-4c80-b971-9d403135eea2')
def test_max_count_non_integer(self):
- # Creating server with non-integer max_count should fail.
+ """Test creating server with non-integer max_count should fail"""
invalid_max_count = 2.5
self.assertRaises(lib_exc.BadRequest, self.create_test_server,
max_count=invalid_max_count)
@@ -55,7 +56,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('476da616-f1ef-4271-a9b1-b9fc87727cdf')
def test_max_count_less_than_min_count(self):
- # Creating server with max_count less than min_count should fail.
+ """Test creating server with max_count < min_count should fail"""
min_count = 3
max_count = 2
self.assertRaises(lib_exc.BadRequest, self.create_test_server,
diff --git a/tempest/api/compute/servers/test_server_metadata.py b/tempest/api/compute/servers/test_server_metadata.py
index 9d87e1c..9f93e76 100644
--- a/tempest/api/compute/servers/test_server_metadata.py
+++ b/tempest/api/compute/servers/test_server_metadata.py
@@ -14,13 +14,26 @@
# under the License.
from tempest.api.compute import base
+from tempest import config
from tempest.lib import decorators
+CONF = config.CONF
+
+# TODO(stephenfin): Remove these tests once the nova Ussuri branch goes EOL
class ServerMetadataTestJSON(base.BaseV2ComputeTest):
+ """Test server metadata"""
+
create_default_network = True
@classmethod
+ def skip_checks(cls):
+ super(ServerMetadataTestJSON, cls).skip_checks()
+ if not CONF.compute_feature_enabled.xenapi_apis:
+ raise cls.skipException(
+ 'Metadata is read-only on non-Xen-based deployments.')
+
+ @classmethod
def setup_clients(cls):
super(ServerMetadataTestJSON, cls).setup_clients()
cls.client = cls.servers_client
@@ -37,7 +50,10 @@
@decorators.idempotent_id('479da087-92b3-4dcf-aeb3-fd293b2d14ce')
def test_list_server_metadata(self):
- # All metadata key/value pairs for a server should be returned
+ """Test listing server metadata
+
+ All metadata key/value pairs for a server should be returned.
+ """
resp_metadata = (self.client.list_server_metadata(self.server['id'])
['metadata'])
@@ -47,7 +63,10 @@
@decorators.idempotent_id('211021f6-21de-4657-a68f-908878cfe251')
def test_set_server_metadata(self):
- # The server's metadata should be replaced with the provided values
+ """Test setting server metadata
+
+ The server's metadata should be replaced with the provided values
+ """
# Create a new set of metadata for the server
req_metadata = {'meta2': 'data2', 'meta3': 'data3'}
self.client.set_server_metadata(self.server['id'], req_metadata)
@@ -60,8 +79,10 @@
@decorators.idempotent_id('344d981e-0c33-4997-8a5d-6c1d803e4134')
def test_update_server_metadata(self):
- # The server's metadata values should be updated to the
- # provided values
+ """Test updating server metadata
+
+ The server's metadata values should be updated to the provided values.
+ """
meta = {'key1': 'alt1', 'key3': 'value3'}
self.client.update_server_metadata(self.server['id'], meta)
@@ -73,8 +94,11 @@
@decorators.idempotent_id('0f58d402-e34a-481d-8af8-b392b17426d9')
def test_update_metadata_empty_body(self):
- # The original metadata should not be lost if empty metadata body is
- # passed
+ """Test updating server metadata to empty values
+
+ The original server metadata should not be lost if empty metadata
+ body is passed.
+ """
meta = {}
self.client.update_server_metadata(self.server['id'], meta)
resp_metadata = (self.client.list_server_metadata(self.server['id'])
@@ -84,15 +108,19 @@
@decorators.idempotent_id('3043c57d-7e0e-49a6-9a96-ad569c265e6a')
def test_get_server_metadata_item(self):
- # The value for a specific metadata key should be returned
+ """Test getting specific server metadata item"""
meta = self.client.show_server_metadata_item(self.server['id'],
'key2')['meta']
self.assertEqual('value2', meta['key2'])
@decorators.idempotent_id('58c02d4f-5c67-40be-8744-d3fa5982eb1c')
def test_set_server_metadata_item(self):
- # The item's value should be updated to the provided value
- # Update the metadata value
+ """Test updating specific server metadata item
+
+ The metadata item's value should be updated to the provided value.
+ """
+
+ # Update the metadata value.
meta = {'nova': 'alt'}
self.client.set_server_metadata_item(self.server['id'], 'nova', meta)
@@ -104,7 +132,10 @@
@decorators.idempotent_id('127642d6-4c7b-4486-b7cd-07265a378658')
def test_delete_server_metadata_item(self):
- # The metadata value/key pair should be deleted from the server
+ """Test deleting server metadata item
+
+ The metadata value/key pair should be deleted from the server.
+ """
self.client.delete_server_metadata_item(self.server['id'], 'key1')
# Verify the metadata item has been removed
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index 5688af1..a697b95 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -14,12 +14,17 @@
# under the License.
from tempest.api.compute import base
+from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
+CONF = config.CONF
+
class ServerMetadataNegativeTestJSON(base.BaseV2ComputeTest):
+ """Negative tests of server metadata"""
+
create_default_network = True
@classmethod
@@ -36,6 +41,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('fe114a8f-3a57-4eff-9ee2-4e14628df049')
def test_server_create_metadata_key_too_long(self):
+ """Test creating server with too long metadata key should fail"""
# Attempt to start a server with a meta-data key that is > 255
# characters
@@ -52,7 +58,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('92431555-4d8b-467c-b95b-b17daa5e57ff')
def test_create_server_metadata_blank_key(self):
- # Blank key should trigger an error.
+ """Test creating server with blank metadata key should fail"""
meta = {'': 'data1'}
self.assertRaises(lib_exc.BadRequest,
self.create_test_server,
@@ -61,6 +67,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('4d9cd7a3-2010-4b41-b8fe-3bbf0b169466')
def test_server_metadata_non_existent_server(self):
+ """Test getting metadata item for a non existent server should fail"""
# GET on a non-existent server should not succeed
non_existent_server_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
@@ -71,7 +78,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('f408e78e-3066-4097-9299-3b0182da812e')
def test_list_server_metadata_non_existent_server(self):
- # List metadata on a non-existent server should not succeed
+ """Test listing metadata for a non existent server should fail"""
non_existent_server_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
self.client.list_server_metadata,
@@ -80,8 +87,10 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0025fbd6-a4ba-4cde-b8c2-96805dcfdabc')
def test_wrong_key_passed_in_body(self):
- # Raise BadRequest if key in uri does not match
- # the key passed in body.
+ """Test setting server metadata item with wrong key in body
+
+ Raise BadRequest if key in uri does not match the key passed in body.
+ """
meta = {'testkey': 'testvalue'}
self.assertRaises(lib_exc.BadRequest,
self.client.set_server_metadata_item,
@@ -90,7 +99,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0df38c2a-3d4e-4db5-98d8-d4d9fa843a12')
def test_set_metadata_non_existent_server(self):
- # Set metadata on a non-existent server should not succeed
+ """Test setting metadata for a non existent server should fail"""
+ if not CONF.compute_feature_enabled.xenapi_apis:
+ raise self.skipException(
+ 'Metadata is read-only on non-Xen-based deployments.')
+
non_existent_server_id = data_utils.rand_uuid()
meta = {'meta1': 'data1'}
self.assertRaises(lib_exc.NotFound,
@@ -101,7 +114,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('904b13dc-0ef2-4e4c-91cd-3b4a0f2f49d8')
def test_update_metadata_non_existent_server(self):
- # An update should not happen for a non-existent server
+ """Test updating metadata for a non existent server should fail"""
+ if not CONF.compute_feature_enabled.xenapi_apis:
+ raise self.skipException(
+ 'Metadata is read-only on non-Xen-based deployments.')
+
non_existent_server_id = data_utils.rand_uuid()
meta = {'key1': 'value1', 'key2': 'value2'}
self.assertRaises(lib_exc.NotFound,
@@ -112,7 +129,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('a452f38c-05c2-4b47-bd44-a4f0bf5a5e48')
def test_update_metadata_with_blank_key(self):
- # Blank key should trigger an error
+ """Test updating server metadata to blank key should fail"""
+ if not CONF.compute_feature_enabled.xenapi_apis:
+ raise self.skipException(
+ 'Metadata is read-only on non-Xen-based deployments.')
+
meta = {'': 'data1'}
self.assertRaises(lib_exc.BadRequest,
self.client.update_server_metadata,
@@ -121,7 +142,14 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('6bbd88e1-f8b3-424d-ba10-ae21c45ada8d')
def test_delete_metadata_non_existent_server(self):
- # Should not be able to delete metadata item from a non-existent server
+ """Test deleting metadata item from a non existent server
+
+ Should not be able to delete metadata item from a non-existent server.
+ """
+ if not CONF.compute_feature_enabled.xenapi_apis:
+ raise self.skipException(
+ 'Metadata is read-only on non-Xen-based deployments.')
+
non_existent_server_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
self.client.delete_server_metadata_item,
@@ -131,9 +159,15 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('d8c0a210-a5c3-4664-be04-69d96746b547')
def test_metadata_items_limit(self):
- # A 403 Forbidden or 413 Overlimit (old behaviour) exception
- # will be raised while exceeding metadata items limit for
- # tenant.
+ """Test set/update server metadata over limit should fail
+
+ A 403 Forbidden or 413 Overlimit (old behaviour) exception
+ will be raised while exceeding metadata items limit for project.
+ """
+ if not CONF.compute_feature_enabled.xenapi_apis:
+ raise self.skipException(
+ 'Metadata is read-only on non-Xen-based deployments.')
+
quota_set = self.quotas_client.show_quota_set(
self.tenant_id)['quota_set']
quota_metadata = quota_set['metadata_items']
@@ -157,8 +191,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('96100343-7fa9-40d8-80fa-d29ef588ce1c')
def test_set_server_metadata_blank_key(self):
- # Raise a bad request error for blank key.
- # set_server_metadata will replace all metadata with new value
+ """Test setting server metadata with blank key should fail"""
+ if not CONF.compute_feature_enabled.xenapi_apis:
+ raise self.skipException(
+ 'Metadata is read-only on non-Xen-based deployments.')
+
meta = {'': 'data1'}
self.assertRaises(lib_exc.BadRequest,
self.client.set_server_metadata,
@@ -167,8 +204,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('64a91aee-9723-4863-be44-4c9d9f1e7d0e')
def test_set_server_metadata_missing_metadata(self):
- # Raise a bad request error for a missing metadata field
- # set_server_metadata will replace all metadata with new value
+ """Test setting server metadata without metadata field should fail"""
+ if not CONF.compute_feature_enabled.xenapi_apis:
+ raise self.skipException(
+ 'Metadata is read-only on non-Xen-based deployments.')
+
meta = {'meta1': 'data1'}
self.assertRaises(lib_exc.BadRequest,
self.client.set_server_metadata,
diff --git a/tempest/api/compute/servers/test_server_password.py b/tempest/api/compute/servers/test_server_password.py
index 7b31ede..f61d4fd 100644
--- a/tempest/api/compute/servers/test_server_password.py
+++ b/tempest/api/compute/servers/test_server_password.py
@@ -19,6 +19,8 @@
class ServerPasswordTestJSON(base.BaseV2ComputeTest):
+ """Test server password"""
+
create_default_network = True
@classmethod
@@ -28,8 +30,10 @@
@decorators.idempotent_id('f83b582f-62a8-4f22-85b0-0dee50ff783a')
def test_get_server_password(self):
+ """Test getting password of a server"""
self.servers_client.show_password(self.server['id'])
@decorators.idempotent_id('f8229e8b-b625-4493-800a-bde86ac611ea')
def test_delete_server_password(self):
+ """Test deleting password from a server"""
self.servers_client.delete_password(self.server['id'])
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 1247494..5445113 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -53,9 +53,11 @@
class ServerRescueTestJSON(ServerRescueTestBase):
+ """Test server rescue"""
@decorators.idempotent_id('fd032140-714c-42e4-a8fd-adcd8df06be6')
def test_rescue_unrescue_instance(self):
+ """Test rescue/unrescue server"""
password = data_utils.rand_password()
server = self.create_test_server(adminPass=password,
wait_until='ACTIVE')
@@ -68,6 +70,7 @@
class ServerRescueTestJSONUnderV235(ServerRescueTestBase):
+ """Test server rescue with compute microversion less than 2.36"""
max_microversion = '2.35'
@@ -81,7 +84,7 @@
@testtools.skipUnless(CONF.network_feature_enabled.floating_ips,
"Floating ips are not available")
def test_rescued_vm_associate_dissociate_floating_ip(self):
- # Association of floating IP to a rescued vm
+ """Test associate/dissociate floating ip for rescued server"""
floating_ip_body = self.floating_ips_client.create_floating_ip(
pool=CONF.network.floating_network_name)['floating_ip']
self.addCleanup(self.floating_ips_client.delete_floating_ip,
@@ -96,6 +99,7 @@
@decorators.idempotent_id('affca41f-7195-492d-8065-e09eee245404')
def test_rescued_vm_add_remove_security_group(self):
+ """Test add/remove security group to for rescued server"""
# Add Security group
sg = self.create_security_group()
self.servers_client.add_security_group(self.rescued_server_id,
@@ -154,33 +158,43 @@
class ServerStableDeviceRescueTest(BaseServerStableDeviceRescueTest):
+ """Test rescuing server specifying type of device for the rescue disk"""
@decorators.idempotent_id('947004c3-e8ef-47d9-9f00-97b74f9eaf96')
def test_stable_device_rescue_cdrom_ide(self):
+ """Test rescuing server with cdrom and ide as the rescue disk"""
server_id, rescue_image_id = self._create_server_and_rescue_image(
hw_rescue_device='cdrom', hw_rescue_bus='ide')
self._test_stable_device_rescue(server_id, rescue_image_id)
@decorators.idempotent_id('16865750-1417-4854-bcf7-496e6753c01e')
def test_stable_device_rescue_disk_virtio(self):
+ """Test rescuing server with disk and virtio as the rescue disk"""
server_id, rescue_image_id = self._create_server_and_rescue_image(
hw_rescue_device='disk', hw_rescue_bus='virtio')
self._test_stable_device_rescue(server_id, rescue_image_id)
@decorators.idempotent_id('12340157-6306-4745-bdda-cfa019908b48')
def test_stable_device_rescue_disk_scsi(self):
+ """Test rescuing server with disk and scsi as the rescue disk"""
server_id, rescue_image_id = self._create_server_and_rescue_image(
hw_rescue_device='disk', hw_rescue_bus='scsi')
self._test_stable_device_rescue(server_id, rescue_image_id)
@decorators.idempotent_id('647d04cf-ad35-4956-89ab-b05c5c16f30c')
def test_stable_device_rescue_disk_usb(self):
+ """Test rescuing server with disk and usb as the rescue disk"""
server_id, rescue_image_id = self._create_server_and_rescue_image(
hw_rescue_device='disk', hw_rescue_bus='usb')
self._test_stable_device_rescue(server_id, rescue_image_id)
@decorators.idempotent_id('a3772b42-00bf-4310-a90b-1cc6fd3e7eab')
def test_stable_device_rescue_disk_virtio_with_volume_attached(self):
+ """Test rescuing server with volume attached
+
+ Attach a volume to the server and then rescue the server with disk
+ and virtio as the rescue disk.
+ """
server_id, rescue_image_id = self._create_server_and_rescue_image(
hw_rescue_device='disk', hw_rescue_bus='virtio')
server = self.servers_client.show_server(server_id)['server']
@@ -192,12 +206,22 @@
class ServerBootFromVolumeStableRescueTest(BaseServerStableDeviceRescueTest):
+ """Test rescuing server specifying type of device for the rescue disk
+
+ Test rescuing server specifying type of device for the rescue disk with
+ compute microversion greater than 2.86.
+ """
min_microversion = '2.87'
@decorators.attr(type='slow')
@decorators.idempotent_id('48f123cb-922a-4065-8db6-b9a9074a556b')
def test_stable_device_rescue_bfv_blank_volume(self):
+ """Test rescuing server with blank volume as block_device_mapping_v2
+
+ Create a server with block_device_mapping_v2 with blank volume,
+ then rescue the server with disk and virtio as the rescue disk.
+ """
block_device_mapping_v2 = [{
"boot_index": "0",
"source_type": "blank",
@@ -211,6 +235,11 @@
@decorators.attr(type='slow')
@decorators.idempotent_id('e4636333-c928-40fc-98b7-70a23eef4224')
def test_stable_device_rescue_bfv_image_volume(self):
+ """Test rescuing server with blank volume as block_device_mapping_v2
+
+ Create a server with block_device_mapping_v2 with image volume,
+ then rescue the server with disk and virtio as the rescue disk.
+ """
block_device_mapping_v2 = [{
"boot_index": "0",
"source_type": "image",
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index 3a4bd6d..cc013e3 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -25,6 +25,7 @@
class ServersTestJSON(base.BaseV2ComputeTest):
+ """Test servers API"""
create_default_network = True
@classmethod
@@ -37,8 +38,11 @@
enable_instance_password,
'Instance password not available.')
def test_create_server_with_admin_password(self):
- # If an admin password is provided on server creation, the server's
- # root password should be set to that password.
+ """Test creating server with admin password
+
+ If an admin password is provided on server creation, the server's
+ root password should be set to that password.
+ """
server = self.create_test_server(adminPass='testpassword')
self.addCleanup(self.delete_server, server['id'])
@@ -47,8 +51,7 @@
@decorators.idempotent_id('8fea6be7-065e-47cf-89b8-496e6f96c699')
def test_create_with_existing_server_name(self):
- # Creating a server with a name that already exists is allowed
-
+ """Test creating a server with already existing name is allowed"""
# TODO(sdague): clear out try, we do cleanup one layer up
server_name = data_utils.rand_name(
self.__class__.__name__ + '-server')
@@ -69,8 +72,7 @@
@decorators.idempotent_id('f9e15296-d7f9-4e62-b53f-a04e89160833')
def test_create_specify_keypair(self):
- # Specify a keypair while creating a server
-
+ """Test creating server with keypair"""
key_name = data_utils.rand_name('key')
self.keypairs_client.create_keypair(name=key_name)
self.addCleanup(self.keypairs_client.delete_keypair, key_name)
@@ -97,7 +99,7 @@
@decorators.idempotent_id('5e6ccff8-349d-4852-a8b3-055df7988dd2')
def test_update_server_name(self):
- # The server name should be changed to the provided value
+ """Test updating server name to the provided value"""
server = self.create_test_server(wait_until='ACTIVE')
self.addCleanup(self.delete_server, server['id'])
# Update instance name with non-ASCII characters
@@ -115,7 +117,7 @@
@decorators.idempotent_id('89b90870-bc13-4b73-96af-f9d4f2b70077')
def test_update_access_server_address(self):
- # The server's access addresses should reflect the provided values
+ """Test updating server's access addresses to the provided value"""
server = self.create_test_server(wait_until='ACTIVE')
self.addCleanup(self.delete_server, server['id'])
@@ -132,7 +134,7 @@
@decorators.idempotent_id('38fb1d02-c3c5-41de-91d3-9bc2025a75eb')
def test_create_server_with_ipv6_addr_only(self):
- # Create a server without an IPv4 address(only IPv6 address).
+ """Test creating server with ipv6 address only(no ipv4 address)"""
server = self.create_test_server(accessIPv6='2001:2001::3',
wait_until='ACTIVE')
self.addCleanup(self.delete_server, server['id'])
@@ -142,17 +144,22 @@
@decorators.related_bug('1730756')
@decorators.idempotent_id('defbaca5-d611-49f5-ae21-56ee25d2db49')
def test_create_server_specify_multibyte_character_name(self):
- # prefix character is:
- # http://unicode.org/cldr/utility/character.jsp?a=20A1
+ """Test creating server with multi character name
- # We use a string with 3 byte utf-8 character due to nova
- # will return 400(Bad Request) if we attempt to send a name which has
- # 4 byte utf-8 character.
+ prefix character is:
+ http://unicode.org/cldr/utility/character.jsp?a=20A1
+
+ We use a string with 3 byte utf-8 character due to nova
+ will return 400(Bad Request) if we attempt to send a name which has
+ 4 byte utf-8 character.
+ """
utf8_name = data_utils.rand_name(b'\xe2\x82\xa1'.decode('utf-8'))
self.create_test_server(name=utf8_name, wait_until='ACTIVE')
class ServerShowV247Test(base.BaseV2ComputeTest):
+ """Test servers API with compute microversion greater than 2.46"""
+
min_microversion = '2.47'
max_microversion = 'latest'
@@ -164,12 +171,14 @@
@decorators.idempotent_id('88b0bdb2-494c-11e7-a919-92ebcb67fe33')
def test_show_server(self):
+ """Test getting server detail"""
server = self.create_test_server()
# All fields will be checked by API schema
self.servers_client.show_server(server['id'])
@decorators.idempotent_id('8de397c2-57d0-4b90-aa30-e5d668f21a8b')
def test_update_rebuild_list_server(self):
+ """Test update/rebuild/list server"""
server = self.create_test_server()
# Checking update API response schema
self.servers_client.update_server(server['id'])
@@ -184,6 +193,8 @@
class ServerShowV263Test(base.BaseV2ComputeTest):
+ """Test servers API with compute microversion greater than 2.62"""
+
min_microversion = '2.63'
max_microversion = 'latest'
@@ -195,6 +206,7 @@
'required to test image certificate validation.')
@decorators.idempotent_id('71b8e3d5-11d2-494f-b917-b094a4afed3c')
def test_show_update_rebuild_list_server(self):
+ """Test show/update/rebuild/list server"""
trusted_certs = CONF.compute.certified_image_trusted_certs
server = self.create_test_server(
image_id=CONF.compute.certified_image_ref,
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 6676358..4f85048 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -60,7 +60,8 @@
server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
- server = cls.create_test_server()
+ # Wait until the instance is active to avoid the delete racing
+ server = cls.create_test_server(wait_until='ACTIVE')
cls.client.delete_server(server['id'])
waiters.wait_for_server_termination(cls.client, server['id'])
cls.deleted_server_id = server['id']
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index dfd6ca4..b2e02c5 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -28,6 +28,8 @@
# TODO(mriedem): Remove this test class once the nova queens branch goes into
# extended maintenance mode.
class VirtualInterfacesTestJSON(base.BaseV2ComputeTest):
+ """Test virtual interfaces API with compute microversion less than 2.44"""
+
max_microversion = '2.43'
depends_on_nova_network = True
@@ -47,9 +49,7 @@
@decorators.idempotent_id('96c4e2ef-5e4d-4d7f-87f5-fed6dca18016')
@utils.services('network')
def test_list_virtual_interfaces(self):
- # Positive test:Should be able to GET the virtual interfaces list
- # for a given server_id
-
+ """Test listing virtual interfaces of a server"""
if CONF.service_available.neutron:
with testtools.ExpectedException(exceptions.BadRequest):
self.client.list_virtual_interfaces(self.server['id'])
diff --git a/tempest/api/compute/servers/test_virtual_interfaces_negative.py b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
index f6e8bc9..5667281 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces_negative.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
@@ -23,6 +23,12 @@
# TODO(mriedem): Remove this test class once the nova queens branch goes into
# extended maintenance mode.
class VirtualInterfacesNegativeTestJSON(base.BaseV2ComputeTest):
+ """Negative tests of virtual interfaces API
+
+ Negative tests of virtual interfaces API for compute microversion less
+ than 2.44.
+ """
+
max_microversion = '2.43'
depends_on_nova_network = True
@@ -37,8 +43,7 @@
@decorators.idempotent_id('64ebd03c-1089-4306-93fa-60f5eb5c803c')
@utils.services('network')
def test_list_virtual_interfaces_invalid_server_id(self):
- # Negative test: Should not be able to GET virtual interfaces
- # for an invalid server_id
+ """Test listing virtual interfaces of an invalid server should fail"""
invalid_server_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
self.servers_client.list_virtual_interfaces,
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index a62492d..5fe0e3b 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -20,6 +20,7 @@
class QuotasTestJSON(base.BaseV2ComputeTest):
+ """Test compute quotas"""
@classmethod
def skip_checks(cls):
@@ -59,7 +60,7 @@
@decorators.idempotent_id('f1ef0a97-dbbb-4cca-adc5-c9fbc4f76107')
def test_get_quotas(self):
- # User can get the quota set for it's tenant
+ """Test user can get the compute quota set for it's project"""
expected_quota_set = self.default_quota_set | set(['id'])
quota_set = self.client.show_quota_set(self.tenant_id)['quota_set']
self.assertEqual(quota_set['id'], self.tenant_id)
@@ -75,7 +76,7 @@
@decorators.idempotent_id('9bfecac7-b966-4f47-913f-1a9e2c12134a')
def test_get_default_quotas(self):
- # User can get the default quota set for it's tenant
+ """Test user can get the default compute quota set for it's project"""
expected_quota_set = self.default_quota_set | set(['id'])
quota_set = (self.client.show_default_quota_set(self.tenant_id)
['quota_set'])
@@ -85,7 +86,7 @@
@decorators.idempotent_id('cd65d997-f7e4-4966-a7e9-d5001b674fdc')
def test_compare_tenant_quotas_with_default_quotas(self):
- # Tenants are created with the default quota values
+ """Test tenants are created with the default compute quota values"""
default_quota_set = \
self.client.show_default_quota_set(self.tenant_id)['quota_set']
tenant_quota_set = (self.client.show_quota_set(self.tenant_id)
diff --git a/tempest/api/compute/volumes/test_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
index 9a506af..516f599 100644
--- a/tempest/api/compute/volumes/test_attach_volume_negative.py
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -21,6 +21,8 @@
class AttachVolumeNegativeTest(base.BaseV2ComputeTest):
+ """Negative tests of volume attaching"""
+
create_default_network = True
@classmethod
@@ -34,6 +36,7 @@
@decorators.related_bug('1630783', status_code=500)
@decorators.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
def test_delete_attached_volume(self):
+ """Test deleting attachemd volume should fail"""
server = self.create_test_server(wait_until='ACTIVE')
volume = self.create_volume()
self.attach_volume(server, volume)
@@ -44,10 +47,13 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('aab919e2-d992-4cbb-a4ed-745c2475398c')
def test_attach_attached_volume_to_same_server(self):
- # Test attaching the same volume to the same instance once
- # it's already attached. The nova/cinder validation for this differs
- # depending on whether or not cinder v3.27 is being used to attach
- # the volume to the instance.
+ """Test attaching attached volume to same server should fail
+
+ Test attaching the same volume to the same instance once
+ it's already attached. The nova/cinder validation for this differs
+ depending on whether or not cinder v3.27 is being used to attach
+ the volume to the instance.
+ """
server = self.create_test_server(wait_until='ACTIVE')
volume = self.create_volume()
@@ -59,6 +65,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('ee37a796-2afb-11e7-bc0f-fa163e65f5ce')
def test_attach_attached_volume_to_different_server(self):
+ """Test attaching attached volume to different server should fail"""
server1 = self.create_test_server(wait_until='ACTIVE')
volume = self.create_volume()
diff --git a/tempest/api/network/admin/test_floating_ips_admin_actions.py b/tempest/api/network/admin/test_floating_ips_admin_actions.py
index ad7dfb3..a8dae7c 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -62,7 +62,7 @@
This test performs below operations:
1. Create couple floating ips for admin and non-admin users.
2. Verify if admin can access all floating ips including other user
- and non-admin user can only access its own floating ips.
+ and non-admin user can only access its own floating ips.
"""
# Create floating ip from admin user
floating_ip_admin = self.admin_floating_ips_client.create_floatingip(
diff --git a/tempest/api/volume/admin/test_backends_capabilities.py b/tempest/api/volume/admin/test_backends_capabilities.py
index 1351704..3c76eca 100644
--- a/tempest/api/volume/admin/test_backends_capabilities.py
+++ b/tempest/api/volume/admin/test_backends_capabilities.py
@@ -20,6 +20,7 @@
class BackendsCapabilitiesAdminTestsJSON(base.BaseVolumeAdminTest):
+ """Test backends capabilities"""
@classmethod
def resource_setup(cls):
@@ -32,14 +33,16 @@
@decorators.idempotent_id('3750af44-5ea2-4cd4-bc3e-56e7e6caf854')
def test_get_capabilities_backend(self):
- # Test backend properties
+ """Test getting backend capabilities"""
# Check response schema
self.admin_capabilities_client.show_backend_capabilities(self.hosts[0])
@decorators.idempotent_id('a9035743-d46a-47c5-9cb7-3c80ea16dea0')
def test_compare_volume_stats_values(self):
- # Test values comparison between show_backend_capabilities
- # to show_pools
+ """Test comparing volume stats values
+
+ Compare volume stats between show_backend_capabilities and show_pools.
+ """
VOLUME_STATS = ('vendor_name',
'volume_backend_name',
'storage_protocol')
diff --git a/tempest/api/volume/admin/test_group_snapshots.py b/tempest/api/volume/admin/test_group_snapshots.py
index c57766e..0a8b56d 100644
--- a/tempest/api/volume/admin/test_group_snapshots.py
+++ b/tempest/api/volume/admin/test_group_snapshots.py
@@ -62,12 +62,26 @@
class GroupSnapshotsTest(BaseGroupSnapshotsTest):
+ """Test group snapshot"""
+
_api_version = 3
min_microversion = '3.14'
max_microversion = 'latest'
@decorators.idempotent_id('1298e537-f1f0-47a3-a1dd-8adec8168897')
def test_group_snapshot_create_show_list_delete(self):
+ """Test create/show/list/delete group snapshot
+
+ 1. Create volume type "volume_type1"
+ 2. Create group type "group_type1"
+ 3. Create group "group1" with "group_type1" and "volume_type1"
+ 4. Create volume "volume1" with "volume_type1" and "group1"
+ 5. Create group snapshot "group_snapshot1" with "group1"
+ 6. Check snapshot created from "volume1" reaches available status
+ 7. Check the created group snapshot "group_snapshot1" is in the list
+ of all group snapshots
+ 8. Delete group snapshot "group_snapshot1"
+ """
# Create volume type
volume_type = self.create_volume_type()
@@ -118,6 +132,18 @@
@decorators.idempotent_id('eff52c70-efc7-45ed-b47a-4ad675d09b81')
def test_create_group_from_group_snapshot(self):
+ """Test creating group from group snapshot
+
+ 1. Create volume type "volume_type1"
+ 2. Create group type "group_type1"
+ 3. Create group "group1" with "group_type1" and "volume_type1"
+ 4. Create volume "volume1" with "volume_type1" and "group1"
+ 5. Create group snapshot "group_snapshot1" with "group1"
+ 6. Check snapshot created from "volume1" reaches available status
+ 7. Create group "group2" from "group_snapshot1"
+ 8. Check the volumes belonging to "group2" reach available status
+ 9. Check "group2" reaches available status
+ """
# Create volume type
volume_type = self.create_volume_type()
@@ -161,6 +187,20 @@
@decorators.idempotent_id('7d7fc000-0b4c-4376-a372-544116d2e127')
@decorators.related_bug('1739031')
def test_delete_group_snapshots_following_updated_volumes(self):
+ """Test deleting group snapshot following updated volumes
+
+ 1. Create volume type "volume_type1"
+ 2. Create group type "group_type1"
+ 3. Create group "group1" with "group_type1" and "volume_type1"
+ 4. Create 2 volumes "volume1" and "volume2"
+ with "volume_type1" and "group1"
+ 5. For each created volume, removing and then adding back to "group1"
+ 6. Create group snapshot "group_snapshot1" with "group1"
+ 7. Check snapshots created from "volume1" and "volume2" reach
+ available status
+ 8. Delete "group_snapshot1"
+ 9. Check snapshots created from "volume1" and "volume2" are deleted
+ """
volume_type = self.create_volume_type()
group_type = self.create_group_type()
@@ -211,6 +251,8 @@
class GroupSnapshotsV319Test(BaseGroupSnapshotsTest):
+ """Test group snapshot with volume microversion greater than 3.18"""
+
_api_version = 3
min_microversion = '3.19'
max_microversion = 'latest'
@@ -218,6 +260,7 @@
@decorators.idempotent_id('3b42c9b9-c984-4444-816e-ca2e1ed30b40')
@decorators.skip_because(bug='1770179')
def test_reset_group_snapshot_status(self):
+ """Test resetting group snapshot status to creating/available/error"""
# Create volume type
volume_type = self.create_volume_type()
diff --git a/tempest/api/volume/admin/test_group_type_specs.py b/tempest/api/volume/admin/test_group_type_specs.py
index c5e6d1a..159c6fb 100644
--- a/tempest/api/volume/admin/test_group_type_specs.py
+++ b/tempest/api/volume/admin/test_group_type_specs.py
@@ -19,12 +19,15 @@
class GroupTypeSpecsTest(base.BaseVolumeAdminTest):
+ """Test group type specs"""
+
_api_version = 3
min_microversion = '3.11'
max_microversion = 'latest'
@decorators.idempotent_id('bb4e30d0-de6e-4f4d-866c-dcc48d023b4e')
def test_group_type_specs_create_show_update_list_delete(self):
+ """Test create/show/update/list/delete group type specs"""
# Create new group type
group_type = self.create_group_type()
diff --git a/tempest/api/volume/admin/test_group_types.py b/tempest/api/volume/admin/test_group_types.py
index 6723207..3993020 100644
--- a/tempest/api/volume/admin/test_group_types.py
+++ b/tempest/api/volume/admin/test_group_types.py
@@ -19,13 +19,15 @@
class GroupTypesTest(base.BaseVolumeAdminTest):
+ """Test group types"""
+
_api_version = 3
min_microversion = '3.11'
max_microversion = 'latest'
@decorators.idempotent_id('dd71e5f9-393e-4d4f-90e9-fa1b8d278864')
def test_group_type_create_list_update_show(self):
- # Create/list/show group type.
+ """Test create/list/update/show group type"""
name = data_utils.rand_name(self.__class__.__name__ + '-group-type')
description = data_utils.rand_name("group-type-description")
group_specs = {"consistent_group_snapshot_enabled": "<is> False"}
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index c5c70d2..a5de987 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -21,6 +21,7 @@
class VolumeMultiBackendTest(base.BaseVolumeAdminTest):
+ """Test volume multi backends"""
@classmethod
def skip_checks(cls):
@@ -78,24 +79,49 @@
@decorators.idempotent_id('c1a41f3f-9dad-493e-9f09-3ff197d477cc')
def test_backend_name_reporting(self):
- # get volume id which created by type without prefix
+ """Test backend name reporting for volume when type is without prefix
+
+ 1. Create volume type, with 'volume_backend_name' as extra spec key
+ 2. Create volume using the created volume type
+ 3. Check 'os-vol-host-attr:host' of the volume info, the value should
+ contain '@' character, like 'cinder@CloveStorage#tecs_backend'
+ """
for volume_id in self.volume_id_list_without_prefix:
self._test_backend_name_reporting_by_volume_id(volume_id)
@decorators.idempotent_id('f38e647f-ab42-4a31-a2e7-ca86a6485215')
def test_backend_name_reporting_with_prefix(self):
- # get volume id which created by type with prefix
+ """Test backend name reporting for volume when type is with prefix
+
+ 1. Create volume type, with 'capabilities:volume_backend_name' as
+ extra spec key
+ 2. Create volume using the created volume type
+ 3. Check 'os-vol-host-attr:host' of the volume info, the value should
+ contain '@' character, like 'cinder@CloveStorage#tecs_backend'
+ """
for volume_id in self.volume_id_list_with_prefix:
self._test_backend_name_reporting_by_volume_id(volume_id)
@decorators.idempotent_id('46435ab1-a0af-4401-8373-f14e66b0dd58')
def test_backend_name_distinction(self):
- # get volume ids which created by type without prefix
+ """Test volume backend distinction when type is without prefix
+
+ 1. For each backend, create volume type with 'volume_backend_name'
+ as extra spec key
+ 2. Create volumes using the created volume types
+ 3. Check 'os-vol-host-attr:host' of each created volume is different.
+ """
self._test_backend_name_distinction(self.volume_id_list_without_prefix)
@decorators.idempotent_id('4236305b-b65a-4bfc-a9d2-69cb5b2bf2ed')
def test_backend_name_distinction_with_prefix(self):
- # get volume ids which created by type without prefix
+ """Test volume backend distinction when type is with prefix
+
+ 1. For each backend, create volume type with
+ 'capabilities:volume_backend_name' as extra spec key
+ 2. Create volumes using the created volume types
+ 3. Check 'os-vol-host-attr:host' of each created volume is different.
+ """
self._test_backend_name_distinction(self.volume_id_list_with_prefix)
def _get_volume_host(self, volume_id):
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 41849bc..4fca240 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -22,6 +22,8 @@
class SnapshotsActionsTest(base.BaseVolumeAdminTest):
+ """Test volume snapshot actions"""
+
@classmethod
def skip_checks(cls):
super(SnapshotsActionsTest, cls).skip_checks()
@@ -65,7 +67,7 @@
@decorators.idempotent_id('3e13ca2f-48ea-49f3-ae1a-488e9180d535')
def test_reset_snapshot_status(self):
- # Reset snapshot status to creating
+ """Test resetting snapshot status to creating"""
status = 'creating'
self.admin_snapshots_client.reset_snapshot_status(
self.snapshot['id'], status)
@@ -74,6 +76,10 @@
@decorators.idempotent_id('41288afd-d463-485e-8f6e-4eea159413eb')
def test_update_snapshot_status(self):
+ """Test updating snapshot
+
+ Update snapshot status to 'error' and progress to '80%'.
+ """
# Reset snapshot status to creating
status = 'creating'
self.admin_snapshots_client.reset_snapshot_status(
@@ -95,20 +101,20 @@
@decorators.idempotent_id('05f711b6-e629-4895-8103-7ca069f2073a')
def test_snapshot_force_delete_when_snapshot_is_creating(self):
- # test force delete when status of snapshot is creating
+ """Test force delete when status of snapshot is creating"""
self._create_reset_and_force_delete_temp_snapshot('creating')
@decorators.idempotent_id('92ce8597-b992-43a1-8868-6316b22a969e')
def test_snapshot_force_delete_when_snapshot_is_deleting(self):
- # test force delete when status of snapshot is deleting
+ """Test force delete when status of snapshot is deleting"""
self._create_reset_and_force_delete_temp_snapshot('deleting')
@decorators.idempotent_id('645a4a67-a1eb-4e8e-a547-600abac1525d')
def test_snapshot_force_delete_when_snapshot_is_error(self):
- # test force delete when status of snapshot is error
+ """Test force delete when status of snapshot is error"""
self._create_reset_and_force_delete_temp_snapshot('error')
@decorators.idempotent_id('bf89080f-8129-465e-9327-b2f922666ba5')
def test_snapshot_force_delete_when_snapshot_is_error_deleting(self):
- # test force delete when status of snapshot is error_deleting
+ """Test force delete when status of snapshot is error_deleting"""
self._create_reset_and_force_delete_temp_snapshot('error_deleting')
diff --git a/tempest/api/volume/admin/test_volume_quota_classes.py b/tempest/api/volume/admin/test_volume_quota_classes.py
index ee52354..f482788 100644
--- a/tempest/api/volume/admin/test_volume_quota_classes.py
+++ b/tempest/api/volume/admin/test_volume_quota_classes.py
@@ -30,6 +30,7 @@
class VolumeQuotaClassesTest(base.BaseVolumeAdminTest):
+ """Test volume quota classes"""
def setUp(self):
# Note(jeremy.zhang): All test cases in this class need to externally
@@ -44,6 +45,7 @@
@decorators.idempotent_id('abb9198e-67d0-4b09-859f-4f4a1418f176')
def test_show_default_quota(self):
+ """Test showing default volume quota class set"""
# response body is validated by schema
default_quotas = self.admin_quota_classes_client.show_quota_class_set(
'default')['quota_class_set']
@@ -51,6 +53,11 @@
@decorators.idempotent_id('a7644c63-2669-467a-b00e-452dd5c5397b')
def test_update_default_quota(self):
+ """Test updating default volume quota class set
+
+ Check current project and new project's default quota are updated
+ to the provided one.
+ """
LOG.debug("Get the current default quota class values")
body = self.admin_quota_classes_client.show_quota_class_set(
'default')['quota_class_set']
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index b073604..5ab8e87 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -22,6 +22,8 @@
class VolumeQuotasAdminTestJSON(base.BaseVolumeAdminTest):
+ """Test volume quotas with admin privilege"""
+
credentials = ['primary', 'alt', 'admin']
def setUp(self):
@@ -54,17 +56,19 @@
@decorators.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
def test_list_quotas(self):
+ """Test showing volume quota set"""
# Check response schema
self.admin_quotas_client.show_quota_set(self.demo_tenant_id)
@decorators.idempotent_id('2be020a2-5fdd-423d-8d35-a7ffbc36e9f7')
def test_list_default_quotas(self):
+ """Test showing volume default quota set"""
# Check response schema
self.admin_quotas_client.show_default_quota_set(self.demo_tenant_id)
@decorators.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
+ """Test admin can update all the volume quota limits for a project"""
new_quota_set = {'gigabytes': 1009,
'volumes': 11,
'snapshots': 11,
@@ -87,14 +91,14 @@
@decorators.idempotent_id('18c51ae9-cb03-48fc-b234-14a19374dbed')
def test_show_quota_usage(self):
+ """Test showing volume quota usage"""
# Check response schema
self.admin_quotas_client.show_quota_set(
self.os_admin.credentials.tenant_id, params={'usage': True})
@decorators.idempotent_id('874b35a9-51f1-4258-bec5-cd561b6690d3')
def test_delete_quota(self):
- # Admin can delete the resource quota set for a project
-
+ """Test admin can delete the volume quota set for a project"""
self.addCleanup(self.admin_quotas_client.update_quota_set,
self.demo_tenant_id, **self.cleanup_quota_set)
@@ -112,6 +116,7 @@
@decorators.idempotent_id('ae8b6091-48ad-4bfa-a188-bbf5cc02115f')
def test_quota_usage(self):
+ """Test volume quota usage is updated after creating volume"""
quota_usage = self.admin_quotas_client.show_quota_set(
self.demo_tenant_id, params={'usage': True})['quota_set']
@@ -131,6 +136,7 @@
@decorators.idempotent_id('8911036f-9d54-4720-80cc-a1c9796a8805')
def test_quota_usage_after_volume_transfer(self):
+ """Test volume quota usage is updated after transferring volume"""
# Create a volume for transfer
volume = self.create_volume()
self.addCleanup(self.delete_volume,
diff --git a/tempest/api/volume/admin/test_volume_retype.py b/tempest/api/volume/admin/test_volume_retype.py
index 18e0b9b..5c14d52 100644
--- a/tempest/api/volume/admin/test_volume_retype.py
+++ b/tempest/api/volume/admin/test_volume_retype.py
@@ -88,6 +88,7 @@
class VolumeRetypeWithMigrationTest(VolumeRetypeTest):
+ """Test volume retype with migration"""
@classmethod
def skip_checks(cls):
@@ -134,11 +135,25 @@
@decorators.idempotent_id('a1a41f3f-9dad-493e-9f09-3ff197d477cd')
def test_available_volume_retype_with_migration(self):
+ """Test volume retype with migration
+
+ 1. Create volume1 with volume_type1
+ 2. Retype volume1 to volume_type2 with migration_policy='on-demand'
+ 3. Check volume1's volume_type is changed to volume_type2, and
+ 'os-vol-host-attr:host' in the volume info is changed.
+ """
src_vol = self.create_volume(volume_type=self.src_vol_type['name'])
self._retype_volume(src_vol, migration_policy='on-demand')
@decorators.idempotent_id('d0d9554f-e7a5-4104-8973-f35b27ccb60d')
def test_volume_from_snapshot_retype_with_migration(self):
+ """Test volume created from snapshot retype with migration
+
+ 1. Create volume1 from snapshot with volume_type1
+ 2. Retype volume1 to volume_type2 with migration_policy='on-demand'
+ 3. Check volume1's volume_type is changed to volume_type2, and
+ 'os-vol-host-attr:host' in the volume info is changed.
+ """
src_vol = self._create_volume_from_snapshot()
# Migrate the volume from snapshot to the second backend
@@ -146,6 +161,7 @@
class VolumeRetypeWithoutMigrationTest(VolumeRetypeTest):
+ """Test volume retype without migration"""
@classmethod
def resource_setup(cls):
@@ -174,6 +190,13 @@
@decorators.idempotent_id('b90412ee-465d-46e9-b249-ec84a47d5f25')
def test_available_volume_retype(self):
+ """Test volume retype without migration
+
+ 1. Create volume1 with volume_type1
+ 2. Retype volume1 to volume_type2 with migration_policy='never'
+ 3. Check volume1's volume_type is changed to volume_type2, and
+ 'os-vol-host-attr:host' in the volume info is not changed.
+ """
src_vol = self.create_volume(volume_type=self.src_vol_type['name'])
# Retype the volume from snapshot
diff --git a/tempest/api/volume/admin/test_volume_services_negative.py b/tempest/api/volume/admin/test_volume_services_negative.py
index 3a863a1..bf39be5 100644
--- a/tempest/api/volume/admin/test_volume_services_negative.py
+++ b/tempest/api/volume/admin/test_volume_services_negative.py
@@ -19,6 +19,7 @@
class VolumeServicesNegativeTest(base.BaseVolumeAdminTest):
+ """Negative tests of volume services"""
@classmethod
def resource_setup(cls):
@@ -30,6 +31,7 @@
@decorators.attr(type='negative')
@decorators.idempotent_id('3246ce65-ba70-4159-aa3b-082c28e4b484')
def test_enable_service_with_invalid_host(self):
+ """Test enabling volume service with invalid host should fail"""
self.assertRaises(lib_exc.NotFound,
self.admin_volume_services_client.enable_service,
host='invalid_host', binary=self.binary)
@@ -37,6 +39,7 @@
@decorators.attr(type='negative')
@decorators.idempotent_id('c571f179-c6e6-4c50-a0ab-368b628a8ac1')
def test_disable_service_with_invalid_binary(self):
+ """Test disabling volume service with invalid binary should fail"""
self.assertRaises(lib_exc.NotFound,
self.admin_volume_services_client.disable_service,
host=self.host, binary='invalid_binary')
@@ -44,6 +47,7 @@
@decorators.attr(type='negative')
@decorators.idempotent_id('77767b36-5e8f-4c68-a0b5-2308cc21ec64')
def test_disable_log_reason_with_no_reason(self):
+ """Test disabling volume service with none reason should fail"""
self.assertRaises(lib_exc.BadRequest,
self.admin_volume_services_client.disable_log_reason,
host=self.host, binary=self.binary,
@@ -52,6 +56,7 @@
@decorators.attr(type='negative')
@decorators.idempotent_id('712bfab8-1f44-4eb5-a632-fa70bf78f05e')
def test_freeze_host_with_invalid_host(self):
+ """Test freezing volume service with invalid host should fail"""
self.assertRaises(lib_exc.BadRequest,
self.admin_volume_services_client.freeze_host,
host='invalid_host')
@@ -59,6 +64,7 @@
@decorators.attr(type='negative')
@decorators.idempotent_id('7c6287c9-d655-47e1-9a11-76f6657a6dce')
def test_thaw_host_with_invalid_host(self):
+ """Test thawing volume service with invalid host should fail"""
self.assertRaises(lib_exc.BadRequest,
self.admin_volume_services_client.thaw_host,
host='invalid_host')
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 ff5e7e2..10fd485 100644
--- a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
@@ -24,6 +24,7 @@
class VolumeSnapshotQuotasNegativeTestJSON(base.BaseVolumeAdminTest):
+ """Negative tests of volume snapshot quotas"""
@classmethod
def skip_checks(cls):
@@ -67,6 +68,7 @@
@decorators.attr(type='negative')
@decorators.idempotent_id('02bbf63f-6c05-4357-9d98-2926a94064ff')
def test_quota_volume_snapshots(self):
+ """Test creating snapshot exceeding snapshots quota should fail"""
self.assertRaises(lib_exc.OverLimit,
self.snapshots_client.create_snapshot,
volume_id=self.volume['id'])
@@ -74,6 +76,7 @@
@decorators.attr(type='negative')
@decorators.idempotent_id('c99a1ca9-6cdf-498d-9fdf-25832babef27')
def test_quota_volume_gigabytes_snapshots(self):
+ """Test creating snapshot exceeding gigabytes quota should fail"""
self.addCleanup(self.admin_quotas_client.update_quota_set,
self.demo_tenant_id,
**self.shared_quota_set)
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index ecc850e..ebcd3b7 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -23,17 +23,18 @@
class VolumeTypesTest(base.BaseVolumeAdminTest):
+ """Test volume types"""
@decorators.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
def test_volume_type_list(self):
- # List volume types.
+ """Test listing volume types"""
body = \
self.admin_volume_types_client.list_volume_types()['volume_types']
self.assertIsInstance(body, list)
@decorators.idempotent_id('c03cc62c-f4e9-4623-91ec-64ce2f9c1260')
def test_volume_crud_with_volume_type_and_extra_specs(self):
- # Create/update/get/delete volume with volume_type and extra spec.
+ """Test create/update/get/delete volume with volume_type"""
volume_types = list()
vol_name = data_utils.rand_name(self.__class__.__name__ + '-volume')
proto = CONF.volume.storage_protocol
@@ -80,7 +81,7 @@
@decorators.idempotent_id('4e955c3b-49db-4515-9590-0c99f8e471ad')
def test_volume_type_create_get_delete(self):
- # Create/get volume type.
+ """Test create/get/delete volume type"""
name = data_utils.rand_name(self.__class__.__name__ + '-volume-type')
description = data_utils.rand_name("volume-type-description")
proto = CONF.volume.storage_protocol
@@ -118,7 +119,7 @@
@decorators.idempotent_id('7830abd0-ff99-4793-a265-405684a54d46')
def test_volume_type_encryption_create_get_update_delete(self):
- # Create/get/update/delete encryption type.
+ """Test create/get/update/delete volume encryption type"""
create_kwargs = {'provider': 'LuksEncryptor',
'control_location': 'front-end'}
volume_type_id = self.create_volume_type()['id']
@@ -175,6 +176,7 @@
@decorators.idempotent_id('cf9f07c6-db9e-4462-a243-5933ad65e9c8')
def test_volume_type_update(self):
+ """Test updating volume type details"""
# Create volume type
volume_type = self.create_volume_type()
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 fe249d6..6b2a278 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
@@ -20,6 +20,7 @@
class ExtraSpecsNegativeTest(base.BaseVolumeAdminTest):
+ """Negative tests of volume type extra specs"""
@classmethod
def resource_setup(cls):
@@ -30,7 +31,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('08961d20-5cbb-4910-ac0f-89ad6dbb2da1')
def test_update_no_body(self):
- # Should not update volume type extra specs with no body
+ """Test updating volume type extra specs with no body should fail"""
self.assertRaises(
lib_exc.BadRequest,
self.admin_volume_types_client.update_volume_type_extra_specs,
@@ -39,7 +40,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('25e5a0ee-89b3-4c53-8310-236f76c75365')
def test_update_nonexistent_extra_spec_id(self):
- # Should not update volume type extra specs with nonexistent id.
+ """Test updating volume type extra specs with non existent name
+
+ Updating volume type extra specs with non existent extra spec name
+ should fail.
+ """
extra_spec = {"spec1": "val2"}
self.assertRaises(
lib_exc.BadRequest,
@@ -50,7 +55,10 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('9bf7a657-b011-4aec-866d-81c496fbe5c8')
def test_update_none_extra_spec_id(self):
- # Should not update volume type extra specs with none id.
+ """Test updating volume type extra specs without name
+
+ Updating volume type extra specs without extra spec name should fail.
+ """
extra_spec = {"spec1": "val2"}
self.assertRaises(
lib_exc.BadRequest,
@@ -60,8 +68,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('a77dfda2-9100-448e-9076-ed1711f4bdfc')
def test_update_multiple_extra_spec(self):
- # Should not update volume type extra specs with multiple specs as
- # body.
+ """Test updating multiple volume type extra specs should fail"""
extra_spec = {"spec1": "val2", "spec2": "val1"}
self.assertRaises(
lib_exc.BadRequest,
@@ -72,8 +79,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('49d5472c-a53d-4eab-a4d3-450c4db1c545')
def test_create_nonexistent_type_id(self):
- # Should not create volume type extra spec for nonexistent volume
- # type id.
+ """Test creating volume type extra specs for non existent volume type
+
+ Creating volume type extra specs for non existent volume type should
+ fail.
+ """
extra_specs = {"spec2": "val1"}
self.assertRaises(
lib_exc.NotFound,
@@ -83,7 +93,10 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('c821bdc8-43a4-4bf4-86c8-82f3858d5f7d')
def test_create_none_body(self):
- # Should not create volume type extra spec for none POST body.
+ """Test creating volume type extra spec with none POST body
+
+ Creating volume type extra spec with none POST body should fail.
+ """
self.assertRaises(
lib_exc.BadRequest,
self.admin_volume_types_client.create_volume_type_extra_specs,
@@ -92,7 +105,10 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('bc772c71-1ed4-4716-b945-8b5ed0f15e87')
def test_create_invalid_body(self):
- # Should not create volume type extra spec for invalid POST body.
+ """Test creating volume type extra spec with invalid POST body
+
+ Creating volume type extra spec with invalid POST body should fail.
+ """
self.assertRaises(
lib_exc.BadRequest,
self.admin_volume_types_client.create_volume_type_extra_specs,
@@ -101,8 +117,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('031cda8b-7d23-4246-8bf6-bbe73fd67074')
def test_delete_nonexistent_volume_type_id(self):
- # Should not delete volume type extra spec for nonexistent
- # type id.
+ """Test deleting volume type extra spec for non existent volume type
+
+ Deleting volume type extra spec for non existent volume type should
+ fail.
+ """
self.assertRaises(
lib_exc.NotFound,
self.admin_volume_types_client.delete_volume_type_extra_specs,
@@ -111,7 +130,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('dee5cf0c-cdd6-4353-b70c-e847050d71fb')
def test_list_nonexistent_volume_type_id(self):
- # Should not list volume type extra spec for nonexistent type id.
+ """Test listing volume type extra spec for non existent volume type
+
+ Listing volume type extra spec for non existent volume type should
+ fail.
+ """
self.assertRaises(
lib_exc.NotFound,
self.admin_volume_types_client.list_volume_types_extra_specs,
@@ -120,7 +143,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('9f402cbd-1838-4eb4-9554-126a6b1908c9')
def test_get_nonexistent_volume_type_id(self):
- # Should not get volume type extra spec for nonexistent type id.
+ """Test getting volume type extra spec for non existent volume type
+
+ Getting volume type extra spec for non existent volume type should
+ fail.
+ """
self.assertRaises(
lib_exc.NotFound,
self.admin_volume_types_client.show_volume_type_extra_specs,
@@ -129,8 +156,11 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('c881797d-12ff-4f1a-b09d-9f6212159753')
def test_get_nonexistent_extra_spec_name(self):
- # Should not get volume type extra spec for nonexistent extra spec
- # name.
+ """Test getting volume type extra spec for non existent spec name
+
+ Getting volume type extra spec for non existent extra spec name should
+ fail.
+ """
self.assertRaises(
lib_exc.NotFound,
self.admin_volume_types_client.show_volume_type_extra_specs,
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index bd4b3fa..835cc1d 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -26,6 +26,7 @@
class VolumesBackupsAdminTest(base.BaseVolumeAdminTest):
+ """Test volume backups"""
@classmethod
def skip_checks(cls):
@@ -118,6 +119,7 @@
@decorators.idempotent_id('47a35425-a891-4e13-961c-c45deea21e94')
def test_volume_backup_reset_status(self):
+ """Test resetting volume backup status to error"""
# Create a volume
volume = self.create_volume()
# Create a backup
diff --git a/tempest/api/volume/test_versions.py b/tempest/api/volume/test_versions.py
index 1e5c9de..e065bdf 100644
--- a/tempest/api/volume/test_versions.py
+++ b/tempest/api/volume/test_versions.py
@@ -17,14 +17,14 @@
class VersionsTest(base.BaseVolumeTest):
- """Test cinder versions"""
+ """Test volume versions"""
_api_version = 3
@decorators.idempotent_id('77838fc4-b49b-4c64-9533-166762517369')
@decorators.attr(type='smoke')
def test_list_versions(self):
- """Test listing cinder versions"""
+ """Test listing volume versions"""
# NOTE: The version data is checked on service client side
# with JSON-Schema validation. It is enough to just call
# the API here.
@@ -32,7 +32,7 @@
@decorators.idempotent_id('7f755ae2-caa9-4049-988c-331d8f7a579f')
def test_show_version(self):
- "Test getting cinder version details"
+ """Test getting volume version details"""
# NOTE: The version data is checked on service client side
# with JSON-Schema validation. So we will loop through each
# version and call show version.
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
index 4d64a95..ccf0804 100644
--- a/tempest/api/volume/test_volume_absolute_limits.py
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -24,6 +24,7 @@
# it requires force_tenant_isolation=True, which need admin
# credentials to create non-admin users for the tests.
class AbsoluteLimitsTests(base.BaseVolumeAdminTest): # noqa: T115
+ """Test volume absolute limits"""
# avoid existing volumes of pre-defined tenant
force_tenant_isolation = True
@@ -43,7 +44,7 @@
@decorators.idempotent_id('8e943f53-e9d6-4272-b2e9-adcf2f7c29ad')
def test_get_volume_absolute_limits(self):
- # get volume limit for a tenant
+ """Test getting volume absolute limits"""
absolute_limits = \
self.volume_limits_client.show_limits(
)['limits']['absolute']
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index e3b8800..2e78114 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -27,6 +27,7 @@
class VolumesBackupsTest(base.BaseVolumeTest):
+ """Test volumes backup"""
@classmethod
def skip_checks(cls):
@@ -54,6 +55,16 @@
'ceph does not support arbitrary container names')
@decorators.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
def test_volume_backup_create_get_detailed_list_restore_delete(self):
+ """Test create/get/list/restore/delete volume backup
+
+ 1. Create volume1 with metadata
+ 2. Create backup1 from volume1
+ 3. Show backup1
+ 4. List backups with detail
+ 5. Restore backup1
+ 6. Verify backup1 has been restored successfully with the metadata
+ of volume1
+ """
# Create a volume with metadata
metadata = {"vol-meta1": "value1",
"vol-meta2": "value2",
@@ -89,7 +100,7 @@
restored_volume_metadata = self.volumes_client.show_volume(
restored_volume['volume_id'])['volume']['metadata']
- # Verify the backups has been restored successfully
+ # Verify the backup has been restored successfully
# with the metadata of the source volume.
self.assertThat(restored_volume_metadata.items(),
matchers.ContainsAll(metadata.items()))
@@ -120,6 +131,13 @@
@decorators.idempotent_id('2a8ba340-dff2-4511-9db7-646f07156b15')
@utils.services('image')
def test_bootable_volume_backup_and_restore(self):
+ """Test backuping and restoring a bootable volume
+
+ 1. Create volume1 from image
+ 2. Create backup1 from volume1
+ 3. Restore backup1
+ 4. Verify the restored backup volume is bootable
+ """
# Create volume from image
img_uuid = CONF.compute.image_ref
volume = self.create_volume(imageRef=img_uuid)
@@ -144,6 +162,7 @@
class VolumesBackupsV39Test(base.BaseVolumeTest):
+ """Test volumes backup with volume microversion greater than 3.8"""
_api_version = 3
min_microversion = '3.9'
@@ -157,6 +176,7 @@
@decorators.idempotent_id('9b374cbc-be5f-4d37-8848-7efb8a873dcc')
def test_update_backup(self):
+ """Test updating backup's name and description"""
# Create volume and backup
volume = self.create_volume()
backup = self.create_backup(volume_id=volume['id'])
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index bf221e8..fd2e7c4 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -25,6 +25,8 @@
class VolumesSnapshotTestJSON(base.BaseVolumeTest):
+ """Test volume snapshots"""
+
create_default_network = True
@classmethod
@@ -41,6 +43,7 @@
@decorators.idempotent_id('8567b54c-4455-446d-a1cf-651ddeaa3ff2')
@utils.services('compute')
def test_snapshot_create_delete_with_volume_in_use(self):
+ """Test create/delete snapshot from volume attached to server"""
# Create a test instance
server = self.create_server()
# NOTE(zhufl) Here we create volume from self.image_ref for adding
@@ -66,7 +69,13 @@
@decorators.idempotent_id('5210a1de-85a0-11e6-bb21-641c676a5d61')
@utils.services('compute')
def test_snapshot_create_offline_delete_online(self):
+ """Test creating snapshots when volume is detached and attached
+ 1. Create snapshot1 from volume1(not attached to any server)
+ 2. Attach volume1 to server1
+ 3. Create snapshot2 and snapshot3 from volume1
+ 4. Delete snapshot3, snapshot1, snapshot2
+ """
# Create a snapshot while it is not attached
snapshot1 = self.create_snapshot(self.volume_origin['id'])
@@ -74,7 +83,7 @@
server = self.create_server()
self.attach_volume(server['id'], self.volume_origin['id'])
- # Now that the volume is attached, create another snapshots
+ # Now that the volume is attached, create other snapshots
snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
@@ -86,6 +95,7 @@
@decorators.idempotent_id('2a8abbe4-d871-46db-b049-c41f5af8216e')
def test_snapshot_create_get_list_update_delete(self):
+ """Test create/get/list/update/delete snapshot"""
# Create a snapshot with metadata
metadata = {"snap-meta1": "value1",
"snap-meta2": "value2",
@@ -156,19 +166,25 @@
@decorators.idempotent_id('677863d1-3142-456d-b6ac-9924f667a7f4')
def test_volume_from_snapshot(self):
- # Creates a volume from a snapshot passing a size
- # different from the source
+ """Test creating volume from snapshot with extending size"""
self._create_volume_from_snapshot(extra_size=1)
@decorators.idempotent_id('053d8870-8282-4fff-9dbb-99cb58bb5e0a')
def test_volume_from_snapshot_no_size(self):
- # Creates a volume from a snapshot defaulting to original size
+ """Test creating volume from snapshot with original size"""
self._create_volume_from_snapshot()
@decorators.idempotent_id('bbcfa285-af7f-479e-8c1a-8c34fc16543c')
@testtools.skipUnless(CONF.volume_feature_enabled.backup,
"Cinder backup is disabled")
def test_snapshot_backup(self):
+ """Test creating backup from snapshot and volume
+
+ 1. Create snapshot1 from volume1
+ 2. Create backup from volume1 and snapshot1
+ 3. Check the created backup's volume is volume1 and snapshot
+ is snapshot1
+ """
# Create a snapshot
snapshot = self.create_snapshot(volume_id=self.volume_origin['id'])
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
index f4f039c..77627bc 100644
--- a/tempest/api/volume/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -18,6 +18,7 @@
class VolumesSnapshotListTestJSON(base.BaseVolumeTest):
+ """Test listing volume snapshots"""
@classmethod
def skip_checks(cls):
@@ -50,6 +51,7 @@
def _list_snapshots_by_param_limit(self, limit, expected_elements):
"""list snapshots by limit param"""
+
# Get snapshots list using limit parameter
fetched_snap_list = self.snapshots_client.list_snapshots(
limit=limit)['snapshots']
@@ -58,7 +60,8 @@
@decorators.idempotent_id('59f41f43-aebf-48a9-ab5d-d76340fab32b')
def test_snapshots_list_with_params(self):
- """list snapshots with params."""
+ """Test listing snapshots with params"""
+
# Verify list snapshots by display_name filter
params = {'name': self.snapshot['name']}
self._list_by_param_values_and_assert(**params)
@@ -74,7 +77,8 @@
@decorators.idempotent_id('220a1022-1fcd-4a74-a7bd-6b859156cda2')
def test_snapshots_list_details_with_params(self):
- """list snapshot details with params."""
+ """Test listing snapshot details with params"""
+
# Verify list snapshot details by display_name filter
params = {'name': self.snapshot['name']}
self._list_by_param_values_and_assert(with_detail=True, **params)
@@ -88,24 +92,29 @@
@decorators.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
def test_snapshot_list_param_limit(self):
- # List returns limited elements
+ """Test listing snapshot with limit returns the limited elements
+
+ If listing snapshots with limit=1, then 1 snapshot is returned.
+ """
self._list_snapshots_by_param_limit(limit=1, expected_elements=1)
@decorators.idempotent_id('a1427f61-420e-48a5-b6e3-0b394fa95400')
def test_snapshot_list_param_limit_equals_infinite(self):
- # List returns all elements when request limit exceeded
- # snapshots number
+ """Test listing snapshot with infinite limit
+
+ If listing snapshots with limit greater than the count of all
+ snapshots, then all snapshots are returned.
+ """
snap_list = self.snapshots_client.list_snapshots()['snapshots']
self._list_snapshots_by_param_limit(limit=100000,
expected_elements=len(snap_list))
@decorators.idempotent_id('e3b44b7f-ae87-45b5-8a8c-66110eb24d0a')
def test_snapshot_list_param_limit_equals_zero(self):
- # List returns zero elements
+ """Test listing snapshot with zero limit should return empty list"""
self._list_snapshots_by_param_limit(limit=0, expected_elements=0)
def _list_snapshots_param_sort(self, sort_key, sort_dir):
- """list snapshots by sort param"""
snap_list = self.snapshots_client.list_snapshots(
sort_key=sort_key, sort_dir=sort_dir)['snapshots']
self.assertNotEmpty(snap_list)
@@ -122,33 +131,42 @@
@decorators.idempotent_id('c5513ada-64c1-4d28-83b9-af3307ec1388')
def test_snapshot_list_param_sort_id_asc(self):
+ """Test listing snapshots sort by id ascendingly"""
self._list_snapshots_param_sort(sort_key='id', sort_dir='asc')
@decorators.idempotent_id('8a7fe058-0b41-402a-8afd-2dbc5a4a718b')
def test_snapshot_list_param_sort_id_desc(self):
+ """Test listing snapshots sort by id descendingly"""
self._list_snapshots_param_sort(sort_key='id', sort_dir='desc')
@decorators.idempotent_id('4052c3a0-2415-440a-a8cc-305a875331b0')
def test_snapshot_list_param_sort_created_at_asc(self):
+ """Test listing snapshots sort by created_at ascendingly"""
self._list_snapshots_param_sort(sort_key='created_at', sort_dir='asc')
@decorators.idempotent_id('dcbbe24a-f3c0-4ec8-9274-55d48db8d1cf')
def test_snapshot_list_param_sort_created_at_desc(self):
+ """Test listing snapshots sort by created_at descendingly"""
self._list_snapshots_param_sort(sort_key='created_at', sort_dir='desc')
@decorators.idempotent_id('d58b5fed-0c37-42d3-8c5d-39014ac13c00')
def test_snapshot_list_param_sort_name_asc(self):
+ """Test listing snapshots sort by display_name ascendingly"""
self._list_snapshots_param_sort(sort_key='display_name',
sort_dir='asc')
@decorators.idempotent_id('96ba6f4d-1f18-47e1-b4bc-76edc6c21250')
def test_snapshot_list_param_sort_name_desc(self):
+ """Test listing snapshots sort by display_name descendingly"""
self._list_snapshots_param_sort(sort_key='display_name',
sort_dir='desc')
@decorators.idempotent_id('05489dde-44bc-4961-a1f5-3ce7ee7824f7')
def test_snapshot_list_param_marker(self):
- # The list of snapshots should end before the provided marker
+ """Test listing snapshots with marker
+
+ The list of snapshots should end before the provided marker
+ """
snap_list = self.snapshots_client.list_snapshots()['snapshots']
# list_snapshots will take the reverse order as they are created.
snapshot_id_list = [snap['id'] for snap in snap_list][::-1]
@@ -163,6 +181,13 @@
@decorators.idempotent_id('ca96d551-17c6-4e11-b0e8-52d3bb8a63c7')
def test_snapshot_list_param_offset(self):
+ """Test listing snapshots with offset and limit
+
+ If listing snapshots with offset=2 and limit=3, then at most 3(limit)
+ snapshots located in the position 2(offset) in the all snapshots list
+ should be returned.
+ (The items in the all snapshots list start from position 0.)
+ """
params = {'offset': 2, 'limit': 3}
snap_list = self.snapshots_client.list_snapshots(**params)['snapshots']
# Verify the list of snapshots skip offset=2 from the first element
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 0453c0a..9c36dc6 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -20,6 +20,7 @@
class VolumesSnapshotNegativeTestJSON(base.BaseVolumeTest):
+ """Negative tests of volume snapshot"""
@classmethod
def skip_checks(cls):
@@ -30,7 +31,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('e3e466af-70ab-4f4b-a967-ab04e3532ea7')
def test_create_snapshot_with_nonexistent_volume_id(self):
- # Create a snapshot with nonexistent volume id
+ """Test creating snapshot from non existent volume should fail"""
s_name = data_utils.rand_name(self.__class__.__name__ + '-snap')
self.assertRaises(lib_exc.NotFound,
self.snapshots_client.create_snapshot,
@@ -40,6 +41,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('bb9da53e-d335-4309-9c15-7e76fd5e4d6d')
def test_create_snapshot_without_passing_volume_id(self):
+ """Test creating snapshot without passing volume_id should fail"""
# Create a snapshot without passing volume id
s_name = data_utils.rand_name(self.__class__.__name__ + '-snap')
self.assertRaises(lib_exc.NotFound,
@@ -49,6 +51,10 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('677863d1-34f9-456d-b6ac-9924f667a7f4')
def test_volume_from_snapshot_decreasing_size(self):
+ """Test creating volume from snapshot with decreasing size
+
+ creating volume from snapshot with decreasing size should fail.
+ """
# Creates a volume a snapshot passing a size different from the source
src_size = CONF.volume.volume_size * 2
@@ -64,6 +70,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('8fd92339-e22f-4591-86b4-1e2215372a40')
def test_list_snapshot_invalid_param_limit(self):
+ """Test listing snapshots with invalid limit param should fail"""
self.assertRaises(lib_exc.BadRequest,
self.snapshots_client.list_snapshots,
limit='invalid')
@@ -71,6 +78,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('27b5f37f-bf69-4e8c-986e-c44f3d6819b8')
def test_list_snapshots_invalid_param_sort(self):
+ """Test listing snapshots with invalid sort key should fail"""
self.assertRaises(lib_exc.BadRequest,
self.snapshots_client.list_snapshots,
sort_key='invalid')
@@ -78,6 +86,7 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('b68deeda-ca79-4a32-81af-5c51179e553a')
def test_list_snapshots_invalid_param_marker(self):
+ """Test listing snapshots with invalid marker should fail"""
self.assertRaises(lib_exc.NotFound,
self.snapshots_client.list_snapshots,
marker=data_utils.rand_uuid())
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 14790d6..fc25914 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -223,6 +223,25 @@
resource_name, resource_id, status, time.time() - start)
+def wait_for_volume_attachment_create(client, volume_id, server_id):
+ """Waits for a volume attachment to be created at a given volume."""
+ start = int(time.time())
+ while True:
+ attachments = client.show_volume(volume_id)['volume']['attachments']
+ found = [a for a in attachments if a['server_id'] == server_id]
+ if found:
+ LOG.info('Attachment %s created for volume %s to server %s after '
+ 'waiting for %f seconds', found[0]['attachment_id'],
+ volume_id, server_id, time.time() - start)
+ return found[0]
+ time.sleep(client.build_interval)
+ if int(time.time()) - start >= client.build_timeout:
+ message = ('Failed to attach volume %s to server %s '
+ 'within the required time (%s s).' %
+ (volume_id, server_id, client.build_timeout))
+ raise lib_exc.TimeoutException(message)
+
+
def wait_for_volume_attachment_remove(client, volume_id, attachment_id):
"""Waits for a volume attachment to be removed from a given volume."""
start = int(time.time())
diff --git a/tempest/config.py b/tempest/config.py
index 989b8ef..a632dee 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -598,6 +598,18 @@
help='Does the test environment support attaching a volume to '
'more than one instance? This depends on hypervisor and '
'volume backend/type and compute API version 2.60.'),
+ cfg.BoolOpt('xenapi_apis',
+ default=False,
+ help='Does the test environment support the XenAPI-specific '
+ 'APIs: os-agents, writeable server metadata and the '
+ 'resetNetwork server action? '
+ 'These were removed in Victoria alongside the XenAPI '
+ 'virt driver.',
+ deprecated_for_removal=True,
+ deprecated_reason="On Nova side, XenAPI virt driver and the "
+ "APIs that only worked with that driver "
+ "have been removed and there's nothing to "
+ "test after Ussuri."),
]
diff --git a/tempest/lib/api_schema/response/volume/backups.py b/tempest/lib/api_schema/response/volume/backups.py
index 9e85f5f..cba7981 100644
--- a/tempest/lib/api_schema/response/volume/backups.py
+++ b/tempest/lib/api_schema/response/volume/backups.py
@@ -66,7 +66,7 @@
'properties': {
'id': {'type': 'string', 'format': 'uuid'},
'links': parameter_types.links,
- 'name': {'type': 'string'},
+ 'name': {'type': ['string', 'null']},
# TODO(zhufl): metadata is added in 3.43, we should move it
# to the 3.43 schema file when microversion is supported
# in volume interfaces.
@@ -91,7 +91,7 @@
'properties': {
'id': {'type': 'string', 'format': 'uuid'},
'links': parameter_types.links,
- 'name': {'type': 'string'},
+ 'name': {'type': ['string', 'null']},
'metadata': {'^.+$': {'type': 'string'}}
},
'additionalProperties': False,
diff --git a/tempest/lib/api_schema/response/volume/manage_volume.py b/tempest/lib/api_schema/response/volume/manage_volume.py
new file mode 100644
index 0000000..d3acfd9
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/manage_volume.py
@@ -0,0 +1,27 @@
+# Copyright 2018 ZTE 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.api_schema.response.volume import volumes
+
+
+manage_volume = {
+ 'status_code': [202],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'volume': volumes.common_show_volume},
+ 'additionalProperties': False,
+ 'required': ['volume']
+ }
+}
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index 808e0fb..ebe2d61 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -124,7 +124,7 @@
def decorator(f):
f = testtools.testcase.attr('id-%s' % id)(f)
if f.__doc__:
- f.__doc__ = 'Test idempotent id: %s\n%s' % (id, f.__doc__)
+ f.__doc__ = 'Test idempotent id: %s\n\n%s' % (id, f.__doc__)
else:
f.__doc__ = 'Test idempotent id: %s' % id
return f
diff --git a/tempest/lib/services/volume/v3/volume_manage_client.py b/tempest/lib/services/volume/v3/volume_manage_client.py
index 85b1b82..f6642c5 100644
--- a/tempest/lib/services/volume/v3/volume_manage_client.py
+++ b/tempest/lib/services/volume/v3/volume_manage_client.py
@@ -15,6 +15,7 @@
from oslo_serialization import jsonutils as json
+from tempest.lib.api_schema.response.volume import manage_volume as schema
from tempest.lib.common import rest_client
@@ -30,6 +31,6 @@
"""
post_body = json.dumps({'volume': kwargs})
resp, body = self.post('os-volume-manage', post_body)
- self.expected_success(202, resp.status)
body = json.loads(body)
+ self.validate_response(schema.manage_volume, resp, body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/test_minbw_allocation_placement.py b/tempest/scenario/test_minbw_allocation_placement.py
index e7085f6..5eab1da 100644
--- a/tempest/scenario/test_minbw_allocation_placement.py
+++ b/tempest/scenario/test_minbw_allocation_placement.py
@@ -124,8 +124,11 @@
resources1='%s:%s' % (self.INGRESS_RESOURCE_CLASS,
self.SMALLEST_POSSIBLE_BW))
if len(alloc_candidates['provider_summaries']) == 0:
- self.fail('No allocation candidates are available for %s:%s' %
- (self.INGRESS_RESOURCE_CLASS, self.SMALLEST_POSSIBLE_BW))
+ # Skip if the backend does not support QoS minimum bandwidth
+ # allocation in Placement API
+ raise self.skipException(
+ 'No allocation candidates are available for %s:%s' %
+ (self.INGRESS_RESOURCE_CLASS, self.SMALLEST_POSSIBLE_BW))
# Just to be sure check with impossible high (placement max_int),
# allocation
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index 5f8b990..73924bd 100755
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -234,6 +234,29 @@
mock.call(volume_id)])
mock_sleep.assert_called_once_with(1)
+ def test_wait_for_volume_attachment_create(self):
+ vol_detached = {'volume': {'attachments': []}}
+ vol_attached = {'volume': {'attachments': [
+ {'id': uuids.volume_id,
+ 'attachment_id': uuids.attachment_id,
+ 'server_id': uuids.server_id,
+ 'volume_id': uuids.volume_id}]}}
+ show_volume = mock.MagicMock(side_effect=[
+ vol_detached, vol_detached, vol_attached])
+ client = mock.Mock(spec=volumes_client.VolumesClient,
+ build_interval=1,
+ build_timeout=5,
+ show_volume=show_volume)
+ self.patch('time.time')
+ self.patch('time.sleep')
+ att = waiters.wait_for_volume_attachment_create(
+ client, uuids.volume_id, uuids.server_id)
+ assert att == vol_attached['volume']['attachments'][0]
+ # Assert that show volume is called until the attachment is removed.
+ show_volume.assert_has_calls([mock.call(uuids.volume_id),
+ mock.call(uuids.volume_id),
+ mock.call(uuids.volume_id)])
+
def test_wait_for_volume_attachment(self):
vol_detached = {'volume': {'attachments': []}}
vol_attached = {'volume': {'attachments': [
@@ -249,9 +272,9 @@
waiters.wait_for_volume_attachment_remove(client, uuids.volume_id,
uuids.attachment_id)
# Assert that show volume is called until the attachment is removed.
- show_volume.assert_has_calls = [mock.call(uuids.volume_id),
- mock.call(uuids.volume_id),
- mock.call(uuids.volume_id)]
+ show_volume.assert_has_calls([mock.call(uuids.volume_id),
+ mock.call(uuids.volume_id),
+ mock.call(uuids.volume_id)])
def test_wait_for_volume_attachment_timeout(self):
show_volume = mock.MagicMock(return_value={
diff --git a/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py b/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
index d4313a2..3d47caf 100644
--- a/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
@@ -54,7 +54,6 @@
}
],
"availability_zone": "nova",
- "os-vol-host-attr:host": "controller1@rbd#rbd",
"encrypted": False,
"updated_at": None,
"replication_status": None,
@@ -62,15 +61,12 @@
"id": "c07cd4a4-b52b-4511-a176-fbaa2011a227",
"size": 0,
"user_id": "142d8663efce464c89811c63e45bd82e",
- "os-vol-tenant-attr:tenant_id": "f21a9c86d7114bf99c711f4874d80474",
- "os-vol-mig-status-attr:migstat": None,
"metadata": {},
"status": "creating",
"description": "volume-manage-description",
"multiattach": False,
"source_volid": None,
"consistencygroup_id": None,
- "os-vol-mig-status-attr:name_id": None,
"name": "volume-managed",
"bootable": "false",
"created_at": "2017-07-11T09:14:01.000000",
diff --git a/tox.ini b/tox.ini
index 0477d6f..031a400 100644
--- a/tox.ini
+++ b/tox.ini
@@ -282,15 +282,31 @@
-r{toxinidir}/requirements.txt
-r{toxinidir}/doc/requirements.txt
commands =
+ sphinx-apidoc -f -o doc/source/tests/compute tempest/api/compute
+ sphinx-apidoc -f -o doc/source/tests/identity tempest/api/identity
+ sphinx-apidoc -f -o doc/source/tests/image tempest/api/image
+ sphinx-apidoc -f -o doc/source/tests/network tempest/api/network
+ sphinx-apidoc -f -o doc/source/tests/object_storage tempest/api/object_storage
+ sphinx-apidoc -f -o doc/source/tests/scenario tempest/scenario
+ sphinx-apidoc -f -o doc/source/tests/volume tempest/api/volume
rm -rf doc/build
sphinx-build -W -b html doc/source doc/build/html
-whitelist_externals = rm
+whitelist_externals =
+ rm
[testenv:pdf-docs]
deps = {[testenv:docs]deps}
whitelist_externals =
+ rm
make
commands =
+ sphinx-apidoc -f -o doc/source/tests/compute tempest/api/compute
+ sphinx-apidoc -f -o doc/source/tests/identity tempest/api/identity
+ sphinx-apidoc -f -o doc/source/tests/image tempest/api/image
+ sphinx-apidoc -f -o doc/source/tests/network tempest/api/network
+ sphinx-apidoc -f -o doc/source/tests/object_storage tempest/api/object_storage
+ sphinx-apidoc -f -o doc/source/tests/scenario tempest/scenario
+ sphinx-apidoc -f -o doc/source/tests/volume tempest/api/volume
sphinx-build -W -b latex doc/source doc/build/pdf
make -C doc/build/pdf