Merge "orchestration add resource limit API test"
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..c9b6467
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,4 @@
+[run]
+branch = True
+source = tempest
+omit = tempest/tests/*,tempest/openstack/*
diff --git a/.gitignore b/.gitignore
index 28a9b9c..1777cb9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,5 @@
build
.testrepository
.coverage*
+!.coveragerc
cover/
diff --git a/HACKING.rst b/HACKING.rst
index c0df0fb..8652971 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -120,13 +120,14 @@
- A json schema: defines properties for a request.
After that a test class must be added to automatically generate test scenarios
-out of the given interface description:
+out of the given interface description::
+
+ load_tests = test.NegativeAutoTest.load_tests
class SampeTestNegativeTestJSON(<your base class>, test.NegativeAutoTest):
_interface = 'json'
_service = 'compute'
- _schema_file = 'compute/servers/get_console_output.json'
- scenarios = test.NegativeAutoTest.generate_scenario(_schema_file)
+ _schema_file = <your Schema file>
Negative tests must be marked with a negative attribute::
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 4a50595..c4dad7e 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -261,7 +261,7 @@
# IP version used for SSH connections. (integer value)
#ip_version_for_ssh=4
-# Dose the SSH uses Floating IP? (boolean value)
+# Does SSH use Floating IPs? (boolean value)
#use_floatingip_for_ssh=true
# Catalog type of the Compute service. (string value)
@@ -958,6 +958,9 @@
# Runs Cinder volumes backup test (boolean value)
#backup=true
+# Runs Cinder volume snapshot test (boolean value)
+#snapshot=true
+
# A list of enabled volume extensions with a special entry all
# which indicates every extension is enabled (list value)
#api_extensions=all
diff --git a/tempest/api/compute/admin/test_flavors_negative.py b/tempest/api/compute/admin/test_flavors_negative.py
index 162e419..b37d32c 100644
--- a/tempest/api/compute/admin/test_flavors_negative.py
+++ b/tempest/api/compute/admin/test_flavors_negative.py
@@ -101,13 +101,9 @@
self.flavor_ref_alt)
+@test.SimpleNegativeAutoTest
class FlavorCreateNegativeTestJSON(base.BaseV2ComputeAdminTest,
test.NegativeAutoTest):
_interface = 'json'
_service = 'compute'
_schema_file = 'compute/admin/flavor_create.json'
-
- @test.attr(type=['negative', 'gate'])
- def test_create_flavor(self):
- # flavor details are not returned for non-existent flavors
- self.execute(self._schema_file)
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 797b780..9fa07f6 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -14,11 +14,16 @@
import uuid
+import testtools
+
from tempest.api.compute import base
from tempest.common.utils import data_utils
+from tempest import config
from tempest import exceptions
from tempest import test
+CONF = config.CONF
+
class ServersAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -119,6 +124,8 @@
self.client.migrate_server,
str(uuid.uuid4()))
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@test.attr(type=['negative', 'gate'])
def test_migrate_server_invalid_state(self):
# create server.
diff --git a/tempest/api/compute/certificates/test_certificates.py b/tempest/api/compute/certificates/test_certificates.py
index 5299d13..f6cadf7 100644
--- a/tempest/api/compute/certificates/test_certificates.py
+++ b/tempest/api/compute/certificates/test_certificates.py
@@ -20,12 +20,15 @@
class CertificatesTestJSON(base.BaseV2ComputeTest):
@test.attr(type='gate')
- def test_create_and_get_root_certificate(self):
+ def test_create_root_certificate(self):
# create certificates
- resp, create_body = self.certificates_client.create_certificate()
+ resp, body = self.certificates_client.create_certificate()
self.assertEqual(200, resp.status)
- self.assertIn('data', create_body)
- self.assertIn('private_key', create_body)
+ self.assertIn('data', body)
+ self.assertIn('private_key', body)
+
+ @test.attr(type='gate')
+ def test_get_root_certificate(self):
# get the root certificate
resp, body = self.certificates_client.get_certificate('root')
self.assertEqual(200, resp.status)
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index a81b7d9..1638f2d 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -21,16 +21,14 @@
load_tests = test.NegativeAutoTest.load_tests
-class FlavorsListNegativeTestJSON(base.BaseV2ComputeTest,
- test.NegativeAutoTest):
+@test.SimpleNegativeAutoTest
+class FlavorsListWithDetailsNegativeTestJSON(base.BaseV2ComputeTest,
+ test.NegativeAutoTest):
_service = 'compute'
_schema_file = 'compute/flavors/flavors_list.json'
- @test.attr(type=['negative', 'gate'])
- def test_list_flavors_with_detail(self):
- self.execute(self._schema_file)
-
+@test.SimpleNegativeAutoTest
class FlavorDetailsNegativeTestJSON(base.BaseV2ComputeTest,
test.NegativeAutoTest):
_service = 'compute'
@@ -40,8 +38,3 @@
def setUpClass(cls):
super(FlavorDetailsNegativeTestJSON, cls).setUpClass()
cls.set_resource("flavor", cls.flavor_ref)
-
- @test.attr(type=['negative', 'gate'])
- def test_get_flavor_details(self):
- # flavor details are not returned for non-existent flavors
- self.execute(self._schema_file)
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index f6eed00..297b300 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -127,7 +127,7 @@
_ifs = self._test_delete_interface(server, ifs)
self.assertEqual(len(ifs) - 1, len(_ifs))
- @test.attr(type='gate')
+ @test.attr(type='smoke')
def test_add_remove_fixed_ip(self):
# Add and Remove the fixed IP to server.
server, ifs = self._create_server_get_interfaces()
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index 7e34213..451d08f 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -23,7 +23,6 @@
class DeleteServersTestJSON(base.BaseV2ComputeTest):
- pause_available = CONF.compute_feature_enabled.pause
# NOTE: Server creations of each test class should be under 10
# for preventing "Quota exceeded for instances"
@@ -59,7 +58,8 @@
self.assertEqual('204', resp['status'])
self.client.wait_for_server_termination(server['id'])
- @testtools.skipIf(not pause_available, 'Pause is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+ 'Pause is not available.')
@test.attr(type='gate')
def test_delete_server_while_in_pause_state(self):
# Delete a server while it's VM state is Pause
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 72ccc71..cc2d1ee 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -29,9 +29,6 @@
class ServerActionsTestJSON(base.BaseV2ComputeTest):
- resize_available = CONF.compute_feature_enabled.resize
- pause_available = CONF.compute_feature_enabled.pause
- suspend_available = CONF.compute_feature_enabled.suspend
run_ssh = CONF.compute.run_ssh
def setUp(self):
@@ -186,7 +183,8 @@
if current_flavor == self.flavor_ref else self.flavor_ref
return current_flavor, new_flavor_ref
- @testtools.skipIf(not resize_available, 'Resize not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+ 'Resize not available.')
@test.attr(type='smoke')
def test_resize_server_confirm(self):
# The server's RAM and disk space should be modified to that of
@@ -205,7 +203,8 @@
resp, server = self.client.get_server(self.server_id)
self.assertEqual(new_flavor_ref, server['flavor']['id'])
- @testtools.skipIf(not resize_available, 'Resize not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+ 'Resize not available.')
@test.attr(type='gate')
def test_resize_server_revert(self):
# The server's RAM and disk space should return to its original
@@ -342,7 +341,8 @@
self.wait_for(self._get_output)
- @testtools.skipIf(not pause_available, 'Pause is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+ 'Pause is not available.')
@test.attr(type='gate')
def test_pause_unpause_server(self):
resp, server = self.client.pause_server(self.server_id)
@@ -352,7 +352,8 @@
self.assertEqual(202, resp.status)
self.client.wait_for_server_status(self.server_id, 'ACTIVE')
- @testtools.skipIf(not suspend_available, 'Suspend is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@test.attr(type='gate')
def test_suspend_resume_server(self):
resp, server = self.client.suspend_server(self.server_id)
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index cbfec5c..cc801b5 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -29,8 +29,6 @@
class ServersNegativeTestJSON(base.BaseV2ComputeTest):
- pause_available = CONF.compute_feature_enabled.pause
- suspend_available = CONF.compute_feature_enabled.suspend
def setUp(self):
super(ServersNegativeTestJSON, self).setUp()
@@ -129,7 +127,8 @@
self.assertRaises(exceptions.NotFound, self.client.reboot,
nonexistent_server, 'SOFT')
- @testtools.skipIf(not pause_available, 'Pause is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+ 'Pause is not available.')
@test.attr(type=['negative', 'gate'])
def test_pause_paused_server(self):
# Pause a paused server.
@@ -309,7 +308,8 @@
self.assertRaises(exceptions.NotFound, self.servers_client.stop,
nonexistent_server)
- @testtools.skipIf(not pause_available, 'Pause is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+ 'Pause is not available.')
@test.attr(type=['negative', 'gate'])
def test_pause_non_existent_server(self):
# pause a non existent server
@@ -317,7 +317,8 @@
self.assertRaises(exceptions.NotFound, self.client.pause_server,
nonexistent_server)
- @testtools.skipIf(not pause_available, 'Pause is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+ 'Pause is not available.')
@test.attr(type=['negative', 'gate'])
def test_unpause_non_existent_server(self):
# unpause a non existent server
@@ -325,7 +326,8 @@
self.assertRaises(exceptions.NotFound, self.client.unpause_server,
nonexistent_server)
- @testtools.skipIf(not pause_available, 'Pause is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+ 'Pause is not available.')
@test.attr(type=['negative', 'gate'])
def test_unpause_server_invalid_state(self):
# unpause an active server.
@@ -333,7 +335,8 @@
self.client.unpause_server,
self.server_id)
- @testtools.skipIf(not suspend_available, 'Suspend is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@test.attr(type=['negative', 'gate'])
def test_suspend_non_existent_server(self):
# suspend a non existent server
@@ -341,7 +344,8 @@
self.assertRaises(exceptions.NotFound, self.client.suspend_server,
nonexistent_server)
- @testtools.skipIf(not suspend_available, 'Suspend is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@test.attr(type=['negative', 'gate'])
def test_suspend_server_invalid_state(self):
# suspend a suspended server.
@@ -354,7 +358,8 @@
self.client.suspend_server,
self.server_id)
- @testtools.skipIf(not suspend_available, 'Suspend is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@test.attr(type=['negative', 'gate'])
def test_resume_non_existent_server(self):
# resume a non existent server
@@ -362,7 +367,8 @@
self.assertRaises(exceptions.NotFound, self.client.resume_server,
nonexistent_server)
- @testtools.skipIf(not suspend_available, 'Suspend is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@test.attr(type=['negative', 'gate'])
def test_resume_server_invalid_state(self):
# resume an active server.
diff --git a/tempest/api/compute/servers/test_servers_negative_new.py b/tempest/api/compute/servers/test_servers_negative_new.py
index f860ff9..43ddb3a 100644
--- a/tempest/api/compute/servers/test_servers_negative_new.py
+++ b/tempest/api/compute/servers/test_servers_negative_new.py
@@ -21,6 +21,7 @@
load_tests = test.NegativeAutoTest.load_tests
+@test.SimpleNegativeAutoTest
class GetConsoleOutputNegativeTestJSON(base.BaseV2ComputeTest,
test.NegativeAutoTest):
_service = 'compute'
@@ -31,7 +32,3 @@
super(GetConsoleOutputNegativeTestJSON, cls).setUpClass()
_resp, server = cls.create_test_server()
cls.set_resource("server", server['id'])
-
- @test.attr(type=['negative', 'gate'])
- def test_get_console_output(self):
- self.execute(self._schema_file)
diff --git a/tempest/api/compute/v3/admin/test_servers_negative.py b/tempest/api/compute/v3/admin/test_servers_negative.py
index cc1be4e..fba4cd1 100644
--- a/tempest/api/compute/v3/admin/test_servers_negative.py
+++ b/tempest/api/compute/v3/admin/test_servers_negative.py
@@ -14,11 +14,16 @@
import uuid
+import testtools
+
from tempest.api.compute import base
from tempest.common.utils import data_utils
+from tempest import config
from tempest import exceptions
from tempest.test import attr
+CONF = config.CONF
+
class ServersAdminNegativeV3Test(base.BaseV3ComputeAdminTest):
@@ -119,6 +124,8 @@
self.client.migrate_server,
str(uuid.uuid4()))
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@attr(type=['negative', 'gate'])
def test_migrate_server_invalid_state(self):
# create server.
diff --git a/tempest/api/compute/v3/certificates/test_certificates.py b/tempest/api/compute/v3/certificates/test_certificates.py
index ce025fc..0ba44cb 100644
--- a/tempest/api/compute/v3/certificates/test_certificates.py
+++ b/tempest/api/compute/v3/certificates/test_certificates.py
@@ -20,12 +20,15 @@
class CertificatesV3Test(base.BaseV3ComputeTest):
@attr(type='gate')
- def test_create_and_get_root_certificate(self):
+ def test_create_root_certificate(self):
# create certificates
- resp, create_body = self.certificates_client.create_certificate()
+ resp, body = self.certificates_client.create_certificate()
self.assertEqual(201, resp.status)
- self.assertIn('data', create_body)
- self.assertIn('private_key', create_body)
+ self.assertIn('data', body)
+ self.assertIn('private_key', body)
+
+ @attr(type='gate')
+ def test_get_root_certificate(self):
# get the root certificate
resp, body = self.certificates_client.get_certificate('root')
self.assertEqual(200, resp.status)
diff --git a/tempest/api/compute/v3/flavors/test_flavors_negative.py b/tempest/api/compute/v3/flavors/test_flavors_negative.py
index 1c0e4fb..657e2cd 100644
--- a/tempest/api/compute/v3/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/v3/flavors/test_flavors_negative.py
@@ -20,16 +20,14 @@
load_tests = test.NegativeAutoTest.load_tests
+@test.SimpleNegativeAutoTest
class FlavorsListNegativeV3Test(base.BaseV3ComputeTest,
test.NegativeAutoTest):
_service = 'computev3'
_schema_file = 'compute/flavors/flavors_list_v3.json'
- @test.attr(type=['negative', 'gate'])
- def test_list_flavors_with_detail(self):
- self.execute(self._schema_file)
-
+@test.SimpleNegativeAutoTest
class FlavorDetailsNegativeV3Test(base.BaseV3ComputeTest,
test.NegativeAutoTest):
_service = 'computev3'
@@ -39,8 +37,3 @@
def setUpClass(cls):
super(FlavorDetailsNegativeV3Test, cls).setUpClass()
cls.set_resource("flavor", cls.flavor_ref)
-
- @test.attr(type=['negative', 'gate'])
- def test_get_flavor_details(self):
- # flavor details are not returned for non-existent flavors
- self.execute(self._schema_file)
diff --git a/tempest/api/compute/v3/servers/test_attach_interfaces.py b/tempest/api/compute/v3/servers/test_attach_interfaces.py
index e1c69d9..c848f8c 100644
--- a/tempest/api/compute/v3/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/v3/servers/test_attach_interfaces.py
@@ -127,7 +127,7 @@
_ifs = self._test_delete_interface(server, ifs)
self.assertEqual(len(ifs) - 1, len(_ifs))
- @attr(type='gate')
+ @attr(type='smoke')
def test_add_remove_fixed_ip(self):
# Add and Remove the fixed IP to server.
server, ifs = self._create_server_get_interfaces()
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 2582fa8..c377c30 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -26,7 +26,6 @@
class ServerActionsV3Test(base.BaseV3ComputeTest):
- resize_available = CONF.compute_feature_enabled.resize
run_ssh = CONF.compute.run_ssh
def setUp(self):
@@ -175,7 +174,8 @@
if current_flavor == self.flavor_ref else self.flavor_ref
return current_flavor, new_flavor_ref
- @testtools.skipIf(not resize_available, 'Resize not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+ 'Resize not available.')
@test.attr(type='smoke')
def test_resize_server_confirm(self):
# The server's RAM and disk space should be modified to that of
@@ -194,7 +194,8 @@
resp, server = self.client.get_server(self.server_id)
self.assertEqual(new_flavor_ref, server['flavor']['id'])
- @testtools.skipIf(not resize_available, 'Resize not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+ 'Resize not available.')
@test.attr(type='gate')
def test_resize_server_revert(self):
# The server's RAM and disk space should return to its original
@@ -337,6 +338,8 @@
self.assertEqual(202, resp.status)
self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@test.attr(type='gate')
def test_suspend_resume_server(self):
resp, server = self.client.suspend_server(self.server_id)
diff --git a/tempest/api/compute/v3/servers/test_servers_negative.py b/tempest/api/compute/v3/servers/test_servers_negative.py
index cb5e93d..586a52a 100644
--- a/tempest/api/compute/v3/servers/test_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_servers_negative.py
@@ -16,6 +16,8 @@
import base64
import sys
+import testtools
+
from tempest.api.compute import base
from tempest import clients
from tempest.common.utils import data_utils
@@ -311,6 +313,8 @@
self.client.unpause_server,
self.server_id)
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@test.attr(type=['negative', 'gate'])
def test_suspend_non_existent_server(self):
# suspend a non existent server
@@ -318,6 +322,8 @@
self.assertRaises(exceptions.NotFound, self.client.suspend_server,
nonexistent_server)
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@test.attr(type=['negative', 'gate'])
def test_suspend_server_invalid_state(self):
# suspend a suspended server.
@@ -337,6 +343,8 @@
self.assertRaises(exceptions.NotFound, self.client.resume_server,
nonexistent_server)
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
@test.attr(type=['negative', 'gate'])
def test_resume_server_invalid_state(self):
# resume an active server.
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
index 73ad22b..84d5be6 100644
--- a/tempest/api/data_processing/base.py
+++ b/tempest/api/data_processing/base.py
@@ -26,9 +26,10 @@
@classmethod
def setUpClass(cls):
super(BaseDataProcessingTest, cls).setUpClass()
- os = cls.get_client_manager()
if not CONF.service_available.sahara:
raise cls.skipException("Sahara support is required")
+
+ os = cls.get_client_manager()
cls.client = os.data_processing_client
# set some constants
@@ -57,7 +58,7 @@
@classmethod
def tearDownClass(cls):
# cleanup node group templates
- for ngt_id in cls._node_group_templates:
+ for ngt_id in getattr(cls, '_node_group_templates', []):
try:
cls.client.delete_node_group_template(ngt_id)
except Exception:
diff --git a/tempest/api/identity/admin/test_tokens.py b/tempest/api/identity/admin/test_tokens.py
index c931bcf..7fec28d 100644
--- a/tempest/api/identity/admin/test_tokens.py
+++ b/tempest/api/identity/admin/test_tokens.py
@@ -72,11 +72,16 @@
self.assertEqual(200, resp.status)
self.data.users.append(user)
- # Create a tenant.
- tenant_name = data_utils.rand_name(name='tenant-')
- resp, tenant = self.client.create_tenant(tenant_name)
+ # Create a couple tenants.
+ tenant1_name = data_utils.rand_name(name='tenant-')
+ resp, tenant1 = self.client.create_tenant(tenant1_name)
self.assertEqual(200, resp.status)
- self.data.tenants.append(tenant)
+ self.data.tenants.append(tenant1)
+
+ tenant2_name = data_utils.rand_name(name='tenant-')
+ resp, tenant2 = self.client.create_tenant(tenant2_name)
+ self.assertEqual(200, resp.status)
+ self.data.tenants.append(tenant2)
# Create a role
role_name = data_utils.rand_name(name='role-')
@@ -84,8 +89,12 @@
self.assertEqual(200, resp.status)
self.data.roles.append(role)
- # Grant the user the role on the tenant.
- resp, _ = self.client.assign_user_role(tenant['id'], user['id'],
+ # Grant the user the role on the tenants.
+ resp, _ = self.client.assign_user_role(tenant1['id'], user['id'],
+ role['id'])
+ self.assertEqual(200, resp.status)
+
+ resp, _ = self.client.assign_user_role(tenant2['id'], user['id'],
role['id'])
self.assertEqual(200, resp.status)
@@ -95,10 +104,20 @@
token_id = body['token']['id']
- # Use the unscoped token to get a scoped token.
- rsp, body = self.token_client.auth_token(token_id, tenant=tenant_name)
+ # Use the unscoped token to get a token scoped to tenant1
+ rsp, body = self.token_client.auth_token(token_id, tenant=tenant1_name)
self.assertEqual(200, resp.status)
+ scoped_token_id = body['token']['id']
+
+ # Revoke the scoped token
+ resp, body = self.client.delete_token(scoped_token_id)
+ self.assertEqual(204, resp.status)
+
+ # Use the unscoped token to get a token scoped to tenant2
+ rsp, body = self.token_client.auth_token(token_id, tenant=tenant2_name)
+ self.assertEqual(204, resp.status)
+
class TokensTestXML(TokensTestJSON):
_interface = 'xml'
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index ab0fb7c..89dd87f 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -53,9 +53,7 @@
@classmethod
def _get_identity_admin_client(cls):
- """
- Returns an instance of the Identity Admin API client
- """
+ """Returns an instance of the Identity Admin API client."""
manager = clients.AdminManager(interface=cls._interface)
admin_client = manager.identity_client
return admin_client
@@ -119,6 +117,6 @@
@staticmethod
def stack_output(stack, output_key):
- """Return a stack output value for a give key."""
+ """Return a stack output value for a given key."""
return next((o['output_value'] for o in stack['outputs']
if o['output_key'] == output_key), None)
diff --git a/tempest/api/orchestration/stacks/test_non_empty_stack.py b/tempest/api/orchestration/stacks/test_non_empty_stack.py
index 70bf562..1da533b 100644
--- a/tempest/api/orchestration/stacks/test_non_empty_stack.py
+++ b/tempest/api/orchestration/stacks/test_non_empty_stack.py
@@ -47,7 +47,7 @@
@attr(type='gate')
def test_stack_list(self):
- """Created stack should be on the list of existing stacks."""
+ """Created stack should be in the list of existing stacks."""
resp, stacks = self.client.list_stacks()
self.assertEqual('200', resp['status'])
self.assertIsInstance(stacks, list)
@@ -76,7 +76,7 @@
@attr(type='gate')
def test_suspend_resume_stack(self):
- """suspend and resume a stack."""
+ """Suspend and resume a stack."""
resp, suspend_stack = self.client.suspend_stack(self.stack_identifier)
self.assertEqual('200', resp['status'])
self.client.wait_for_stack_status(self.stack_identifier,
@@ -121,7 +121,7 @@
@attr(type='gate')
def test_resource_metadata(self):
- """Getting metadata for created resource should be possible."""
+ """Getting metadata for created resources should be possible."""
resp, metadata = self.client.show_resource_metadata(
self.stack_identifier,
self.resource_name)
@@ -147,7 +147,7 @@
@attr(type='gate')
def test_show_event(self):
- """Getting details about existing event should be possible."""
+ """Getting details about an event should be possible."""
resp, events = self.client.list_resource_events(self.stack_identifier,
self.resource_name)
self.assertNotEqual([], events)
diff --git a/tempest/api/orchestration/stacks/test_swift_resources.py b/tempest/api/orchestration/stacks/test_swift_resources.py
index b954128..5948e11 100644
--- a/tempest/api/orchestration/stacks/test_swift_resources.py
+++ b/tempest/api/orchestration/stacks/test_swift_resources.py
@@ -49,7 +49,7 @@
cls.test_resources[resource['logical_resource_id']] = resource
def test_created_resources(self):
- """Created stack should be on the list of existing stacks."""
+ """Created stack should be in the list of existing stacks."""
resources = [('SwiftContainer', 'OS::Swift::Container'),
('SwiftContainerWebsite', 'OS::Swift::Container')]
for resource_name, resource_type in resources:
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 2ce3a4f..6294cd9 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -29,6 +29,9 @@
super(VolumesSnapshotTest, cls).setUpClass()
cls.volume_origin = cls.create_volume()
+ if not CONF.volume_feature_enabled.snapshot:
+ raise cls.skipException("Cinder volume snapshots are disabled")
+
@classmethod
def tearDownClass(cls):
super(VolumesSnapshotTest, cls).tearDownClass()
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 9e47c03..61aa307 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -14,13 +14,23 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
+from tempest import config
from tempest import exceptions
from tempest import test
+CONF = config.CONF
+
class VolumesSnapshotNegativeTest(base.BaseVolumeV1Test):
_interface = "json"
+ @classmethod
+ def setUpClass(cls):
+ super(VolumesSnapshotNegativeTest, cls).setUpClass()
+
+ if not CONF.volume_feature_enabled.snapshot:
+ raise cls.skipException("Cinder volume snapshots are disabled")
+
@test.attr(type=['negative', 'gate'])
def test_create_snapshot_with_nonexistent_volume_id(self):
# Create a snapshot with nonexistent volume id
diff --git a/tempest/api_schema/compute/flavors.py b/tempest/api_schema/compute/flavors.py
new file mode 100644
index 0000000..a6367d4
--- /dev/null
+++ b/tempest/api_schema/compute/flavors.py
@@ -0,0 +1,37 @@
+# Copyright 2014 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api_schema.compute import parameter_types
+
+list_flavors = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'flavors': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'name': {'type': 'string'},
+ 'links': parameter_types.links,
+ 'id': {'type': 'string'}
+ },
+ 'required': ['name', 'links', 'id']
+ }
+ }
+ },
+ 'required': ['flavors']
+ }
+}
diff --git a/tempest/api_schema/compute/hosts.py b/tempest/api_schema/compute/hosts.py
index b9a3db9..a73e214 100644
--- a/tempest/api_schema/compute/hosts.py
+++ b/tempest/api_schema/compute/hosts.py
@@ -33,3 +33,34 @@
'required': ['hosts']
}
}
+
+show_host_detail = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'host': {
+ 'type': 'array',
+ 'item': {
+ 'type': 'object',
+ 'properties': {
+ 'resource': {
+ 'type': 'object',
+ 'properties': {
+ 'cpu': {'type': 'integer'},
+ 'disk_gb': {'type': 'integer'},
+ 'host': {'type': 'string'},
+ 'memory_mb': {'type': 'integer'},
+ 'project': {'type': 'string'}
+ },
+ 'required': ['cpu', 'disk_gb', 'host',
+ 'memory_mb', 'project']
+ }
+ },
+ 'required': ['resource']
+ }
+ }
+ },
+ 'required': ['host']
+ }
+}
diff --git a/tempest/cli/simple_read_only/test_cinder.py b/tempest/cli/simple_read_only/test_cinder.py
index afbd732..723333b 100644
--- a/tempest/cli/simple_read_only/test_cinder.py
+++ b/tempest/cli/simple_read_only/test_cinder.py
@@ -16,6 +16,7 @@
import logging
import re
import subprocess
+import testtools
import tempest.cli
from tempest import config
@@ -86,6 +87,8 @@
def test_cinder_rate_limits(self):
self.cinder('rate-limits')
+ @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+ 'Volume snapshot not available.')
def test_cinder_snapshot_list(self):
self.cinder('snapshot-list')
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 5d7779e..35d4ff2 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -549,7 +549,7 @@
# code if it exists is something that we expect. This is explicitly
# declared in the V3 API and so we should be able to export this in
# the response schema. For now we'll ignore it.
- if str(resp.status).startswith('2'):
+ if resp.status in HTTP_SUCCESS:
response_code = schema['status_code']
if resp.status not in response_code:
msg = ("The status code(%s) is different than the expected "
diff --git a/tempest/config.py b/tempest/config.py
index 778ac23..af96132 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -189,7 +189,7 @@
help="IP version used for SSH connections."),
cfg.BoolOpt('use_floatingip_for_ssh',
default=True,
- help="Dose the SSH uses Floating IP?"),
+ help="Does SSH use Floating IPs?"),
cfg.StrOpt('catalog_type',
default='compute',
help="Catalog type of the Compute service."),
@@ -453,6 +453,9 @@
cfg.BoolOpt('backup',
default=True,
help='Runs Cinder volumes backup test'),
+ cfg.BoolOpt('snapshot',
+ default=True,
+ help='Runs Cinder volume snapshot test'),
cfg.ListOpt('api_extensions',
default=['all'],
help='A list of enabled volume extensions with a special '
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 128ec17..5235871 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -50,6 +50,13 @@
14. Check the existence of a file which created at 6. in volume2
"""
+ @classmethod
+ def setUpClass(cls):
+ super(TestStampPattern, cls).setUpClass()
+
+ if not CONF.volume_feature_enabled.snapshot:
+ raise cls.skipException("Cinder volume snapshots are disabled")
+
def _wait_for_volume_snapshot_status(self, volume_snapshot, status):
self.status_timeout(self.volume_client.volume_snapshots,
volume_snapshot.id, status)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index e89ea70..faca31f 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -35,6 +35,12 @@
* Boot an additional instance from the new snapshot based volume
* Check written content in the instance booted from snapshot
"""
+ @classmethod
+ def setUpClass(cls):
+ super(TestVolumeBootPattern, cls).setUpClass()
+
+ if not CONF.volume_feature_enabled.snapshot:
+ raise cls.skipException("Cinder volume snapshots are disabled")
def _create_volume_from_image(self):
img_uuid = CONF.compute.image_ref
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index bc64117..bc4a64f 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -16,6 +16,7 @@
import json
import urllib
+from tempest.api_schema.compute import flavors as common_schema
from tempest.api_schema.compute import flavors_access as schema_access
from tempest.common import rest_client
from tempest import config
@@ -36,6 +37,7 @@
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(common_schema.list_flavors, resp, body)
return resp, body['flavors']
def list_flavors_with_detail(self, params=None):
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index 0130f27..eeb417a 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -45,6 +45,7 @@
resp, body = self.get("os-hosts/%s" % str(hostname))
body = json.loads(body)
+ self.validate_response(schema.show_host_detail, resp, body)
return resp, body['host']
def update_host(self, hostname, **kwargs):
diff --git a/tempest/services/compute/v3/json/flavors_client.py b/tempest/services/compute/v3/json/flavors_client.py
index 655e279..3fdb3ca 100644
--- a/tempest/services/compute/v3/json/flavors_client.py
+++ b/tempest/services/compute/v3/json/flavors_client.py
@@ -16,6 +16,7 @@
import json
import urllib
+from tempest.api_schema.compute import flavors as common_schema
from tempest.api_schema.compute import flavors_access as schema_access
from tempest.common import rest_client
from tempest import config
@@ -36,6 +37,7 @@
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(common_schema.list_flavors, resp, body)
return resp, body['flavors']
def list_flavors_with_detail(self, params=None):
diff --git a/tempest/services/compute/v3/json/hosts_client.py b/tempest/services/compute/v3/json/hosts_client.py
index bcb9d36..db7134c 100644
--- a/tempest/services/compute/v3/json/hosts_client.py
+++ b/tempest/services/compute/v3/json/hosts_client.py
@@ -45,6 +45,7 @@
resp, body = self.get("os-hosts/%s" % str(hostname))
body = json.loads(body)
+ self.validate_response(schema.show_host_detail, resp, body)
return resp, body['host']
def update_host(self, hostname, **kwargs):
diff --git a/tempest/test.py b/tempest/test.py
index abf42c0..e4019f9 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -17,6 +17,7 @@
import functools
import json
import os
+import re
import sys
import time
import urllib
@@ -581,6 +582,24 @@
return None
+def SimpleNegativeAutoTest(klass):
+ """
+ This decorator registers a test function on basis of the class name.
+ """
+ @attr(type=['negative', 'gate'])
+ def generic_test(self):
+ self.execute(self._schema_file)
+
+ cn = klass.__name__
+ cn = cn.replace('JSON', '')
+ cn = cn.replace('Test', '')
+ # NOTE(mkoderer): replaces uppercase chars inside the class name with '_'
+ lower_cn = re.sub('(?<!^)(?=[A-Z])', '_', cn).lower()
+ func_name = 'test_%s' % lower_cn
+ setattr(klass, func_name, generic_test)
+ return klass
+
+
def call_until_true(func, duration, sleep_for):
"""
Call the given function until it returns True (and return True) or
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index e6a1638..e8610d3 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -220,7 +220,6 @@
# NOTE(afazekas): doctored test case,
# with normal validation it would fail
- @test.skip_because(bug="1182679")
@test.attr(type='smoke')
def test_integration_1(self):
# EC2 1. integration test (not strict)