Merge "keystone trusts API test, move delete into tearDown handler"
diff --git a/requirements.txt b/requirements.txt
index cd11aa7..e070549 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,7 +18,6 @@
keyring>=1.6.1,<2.0
testrepository>=0.0.17
oslo.config>=1.2.0
-eventlet>=0.13.0
six>=1.4.1
iso8601>=0.1.8
fixtures>=0.3.14
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 2809244..908d537 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -16,8 +16,7 @@
# under the License.
from tempest.api.compute import base
-from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class AbsoluteLimitsTestJSON(base.BaseV2ComputeTest):
@@ -27,9 +26,8 @@
def setUpClass(cls):
super(AbsoluteLimitsTestJSON, cls).setUpClass()
cls.client = cls.limits_client
- cls.server_client = cls.servers_client
- @attr(type='gate')
+ @test.attr(type='gate')
def test_absLimits_get(self):
# To check if all limits are present in the response
resp, absolute_limits = self.client.get_absolute_limits()
@@ -49,25 +47,6 @@
"Failed to find element %s in absolute limits list"
% ', '.join(ele for ele in missing_elements))
- @attr(type=['negative', 'gate'])
- def test_max_image_meta_exceed_limit(self):
- # We should not create vm with image meta over maxImageMeta limit
- # Get max limit value
- max_meta = self.client.get_specific_absolute_limit('maxImageMeta')
-
- # Create server should fail, since we are passing > metadata Limit!
- max_meta_data = int(max_meta) + 1
-
- meta_data = {}
- for xx in range(max_meta_data):
- meta_data[str(xx)] = str(xx)
-
- self.assertRaises(exceptions.OverLimit,
- self.server_client.create_server,
- name='test', meta=meta_data,
- flavor_ref=self.flavor_ref,
- image_ref=self.image_ref)
-
class AbsoluteLimitsTestXML(AbsoluteLimitsTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/limits/test_absolute_limits_negative.py b/tempest/api/compute/limits/test_absolute_limits_negative.py
new file mode 100644
index 0000000..8547403
--- /dev/null
+++ b/tempest/api/compute/limits/test_absolute_limits_negative.py
@@ -0,0 +1,53 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest import exceptions
+from tempest import test
+
+
+class AbsoluteLimitsNegativeTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(AbsoluteLimitsNegativeTestJSON, cls).setUpClass()
+ cls.client = cls.limits_client
+ cls.server_client = cls.servers_client
+
+ @test.attr(type=['negative', 'gate'])
+ def test_max_image_meta_exceed_limit(self):
+ # We should not create vm with image meta over maxImageMeta limit
+ # Get max limit value
+ max_meta = self.client.get_specific_absolute_limit('maxImageMeta')
+
+ # Create server should fail, since we are passing > metadata Limit!
+ max_meta_data = int(max_meta) + 1
+
+ meta_data = {}
+ for xx in range(max_meta_data):
+ meta_data[str(xx)] = str(xx)
+
+ self.assertRaises(exceptions.OverLimit,
+ self.server_client.create_server,
+ name='test', meta=meta_data,
+ flavor_ref=self.flavor_ref,
+ image_ref=self.image_ref)
+
+
+class AbsoluteLimitsNegativeTestXML(AbsoluteLimitsNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 95e9171..4ae65be 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -15,17 +15,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-import uuid
-
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
-from tempest.test import skip_because
-
-CONF = config.CONF
+from tempest import test
class SecurityGroupsTestJSON(base.BaseV2ComputeTest):
@@ -35,13 +28,12 @@
def setUpClass(cls):
super(SecurityGroupsTestJSON, cls).setUpClass()
cls.client = cls.security_groups_client
- cls.neutron_available = cls.config.service_available.neutron
def _delete_security_group(self, securitygroup_id):
resp, _ = self.client.delete_security_group(securitygroup_id)
self.assertEqual(202, resp.status)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_security_groups_create_list_delete(self):
# Positive test:Should return the list of Security Groups
# Create 3 Security Groups
@@ -69,7 +61,7 @@
# TODO(afazekas): scheduled for delete,
# test_security_group_create_get_delete covers it
- @attr(type='gate')
+ @test.attr(type='gate')
def test_security_group_create_delete(self):
# Security Group should be created, verified and deleted
s_name = data_utils.rand_name('securitygroup-')
@@ -88,7 +80,7 @@
"The created Security Group name is "
"not equal to the requested name")
- @attr(type='gate')
+ @test.attr(type='gate')
def test_security_group_create_get_delete(self):
# Security Group should be created, fetched and deleted
s_name = data_utils.rand_name('securitygroup-')
@@ -112,121 +104,7 @@
"The fetched Security Group is different "
"from the created Group")
- @attr(type=['negative', 'smoke'])
- def test_security_group_get_nonexistant_group(self):
- # Negative test:Should not be able to GET the details
- # of non-existent Security Group
- security_group_id = []
- resp, body = self.client.list_security_groups()
- for i in range(len(body)):
- security_group_id.append(body[i]['id'])
- # Creating a non-existent Security Group id
- while True:
- non_exist_id = data_utils.rand_int_id(start=999)
- if self.neutron_available:
- non_exist_id = str(uuid.uuid4())
- if non_exist_id not in security_group_id:
- break
- self.assertRaises(exceptions.NotFound, self.client.get_security_group,
- non_exist_id)
-
- @skip_because(bug="1161411",
- condition=CONF.service_available.neutron)
- @attr(type=['negative', 'gate'])
- def test_security_group_create_with_invalid_group_name(self):
- # Negative test: Security Group should not be created with group name
- # as an empty string/with white spaces/chars more than 255
- s_description = data_utils.rand_name('description-')
- # Create Security Group with empty string as group name
- self.assertRaises(exceptions.BadRequest,
- self.client.create_security_group, "", s_description)
- # Create Security Group with white space in group name
- self.assertRaises(exceptions.BadRequest,
- self.client.create_security_group, " ",
- s_description)
- # Create Security Group with group name longer than 255 chars
- s_name = 'securitygroup-'.ljust(260, '0')
- self.assertRaises(exceptions.BadRequest,
- self.client.create_security_group, s_name,
- s_description)
-
- @skip_because(bug="1161411",
- condition=CONF.service_available.neutron)
- @attr(type=['negative', 'gate'])
- def test_security_group_create_with_invalid_group_description(self):
- # Negative test:Security Group should not be created with description
- # as an empty string/with white spaces/chars more than 255
- s_name = data_utils.rand_name('securitygroup-')
- # Create Security Group with empty string as description
- self.assertRaises(exceptions.BadRequest,
- self.client.create_security_group, s_name, "")
- # Create Security Group with white space in description
- self.assertRaises(exceptions.BadRequest,
- self.client.create_security_group, s_name, " ")
- # Create Security Group with group description longer than 255 chars
- s_description = 'description-'.ljust(260, '0')
- self.assertRaises(exceptions.BadRequest,
- self.client.create_security_group, s_name,
- s_description)
-
- @testtools.skipIf(CONF.service_available.neutron,
- "Neutron allows duplicate names for security groups")
- @attr(type=['negative', 'gate'])
- def test_security_group_create_with_duplicate_name(self):
- # Negative test:Security Group with duplicate name should not
- # be created
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, security_group =\
- self.client.create_security_group(s_name, s_description)
- self.assertEqual(200, resp.status)
-
- self.addCleanup(self.client.delete_security_group,
- security_group['id'])
- # Now try the Security Group with the same 'Name'
- self.assertRaises(exceptions.BadRequest,
- self.client.create_security_group, s_name,
- s_description)
-
- @attr(type=['negative', 'gate'])
- def test_delete_the_default_security_group(self):
- # Negative test:Deletion of the "default" Security Group should Fail
- default_security_group_id = None
- resp, body = self.client.list_security_groups()
- for i in range(len(body)):
- if body[i]['name'] == 'default':
- default_security_group_id = body[i]['id']
- break
- # Deleting the "default" Security Group
- self.assertRaises(exceptions.BadRequest,
- self.client.delete_security_group,
- default_security_group_id)
-
- @attr(type=['negative', 'smoke'])
- def test_delete_nonexistant_security_group(self):
- # Negative test:Deletion of a non-existent Security Group should Fail
- security_group_id = []
- resp, body = self.client.list_security_groups()
- for i in range(len(body)):
- security_group_id.append(body[i]['id'])
- # Creating non-existent Security Group
- while True:
- non_exist_id = data_utils.rand_int_id(start=999)
- if self.neutron_available:
- non_exist_id = str(uuid.uuid4())
- if non_exist_id not in security_group_id:
- break
- self.assertRaises(exceptions.NotFound,
- self.client.delete_security_group, non_exist_id)
-
- @attr(type=['negative', 'gate'])
- def test_delete_security_group_without_passing_id(self):
- # Negative test:Deletion of a Security Group with out passing ID
- # should Fail
- self.assertRaises(exceptions.NotFound,
- self.client.delete_security_group, '')
-
- @attr(type='gate')
+ @test.attr(type='gate')
def test_server_security_groups(self):
# Checks that security groups may be added and linked to a server
# and not deleted if the server is active.
@@ -282,6 +160,33 @@
self.client.delete_security_group(sg2_id)
self.assertEqual(202, resp.status)
+ @test.attr(type='gate')
+ def test_update_security_groups(self):
+ # Update security group name and description
+ # Create a security group
+ s_name = data_utils.rand_name('sg-')
+ s_description = data_utils.rand_name('description-')
+ resp, securitygroup = \
+ self.client.create_security_group(s_name, s_description)
+ self.assertEqual(200, resp.status)
+ self.assertIn('id', securitygroup)
+ securitygroup_id = securitygroup['id']
+ self.addCleanup(self._delete_security_group,
+ securitygroup_id)
+ # Update the name and description
+ s_new_name = data_utils.rand_name('sg-hth-')
+ s_new_des = data_utils.rand_name('description-hth-')
+ resp, sg_new = \
+ self.client.update_security_group(securitygroup_id,
+ name=s_new_name,
+ description=s_new_des)
+ self.assertEqual(200, resp.status)
+ # get the security group
+ resp, fetched_group = \
+ self.client.get_security_group(securitygroup_id)
+ self.assertEqual(s_new_name, fetched_group['name'])
+ self.assertEqual(s_new_des, fetched_group['description'])
+
class SecurityGroupsTestXML(SecurityGroupsTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/security_groups/test_security_groups_negative.py b/tempest/api/compute/security_groups/test_security_groups_negative.py
new file mode 100644
index 0000000..6a8e604
--- /dev/null
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -0,0 +1,216 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+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 SecurityGroupsNegativeTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(SecurityGroupsNegativeTestJSON, cls).setUpClass()
+ cls.client = cls.security_groups_client
+ cls.neutron_available = cls.config.service_available.neutron
+
+ def _delete_security_group(self, securitygroup_id):
+ resp, _ = self.client.delete_security_group(securitygroup_id)
+ self.assertEqual(202, resp.status)
+
+ def _generate_a_non_existent_security_group_id(self):
+ security_group_id = []
+ resp, body = self.client.list_security_groups()
+ for i in range(len(body)):
+ security_group_id.append(body[i]['id'])
+ # Generate a non-existent security group id
+ while True:
+ non_exist_id = data_utils.rand_int_id(start=999)
+ if self.neutron_available:
+ non_exist_id = data_utils.rand_uuid()
+ if non_exist_id not in security_group_id:
+ break
+ return non_exist_id
+
+ @test.attr(type=['negative', 'smoke'])
+ def test_security_group_get_nonexistent_group(self):
+ # Negative test:Should not be able to GET the details
+ # of non-existent Security Group
+ non_exist_id = self._generate_a_non_existent_security_group_id()
+ self.assertRaises(exceptions.NotFound, self.client.get_security_group,
+ non_exist_id)
+
+ @test.skip_because(bug="1161411",
+ condition=CONF.service_available.neutron)
+ @test.attr(type=['negative', 'gate'])
+ def test_security_group_create_with_invalid_group_name(self):
+ # Negative test: Security Group should not be created with group name
+ # as an empty string/with white spaces/chars more than 255
+ s_description = data_utils.rand_name('description-')
+ # Create Security Group with empty string as group name
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_security_group, "", s_description)
+ # Create Security Group with white space in group name
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_security_group, " ",
+ s_description)
+ # Create Security Group with group name longer than 255 chars
+ s_name = 'securitygroup-'.ljust(260, '0')
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_security_group, s_name,
+ s_description)
+
+ @test.skip_because(bug="1161411",
+ condition=CONF.service_available.neutron)
+ @test.attr(type=['negative', 'gate'])
+ def test_security_group_create_with_invalid_group_description(self):
+ # Negative test:Security Group should not be created with description
+ # as an empty string/with white spaces/chars more than 255
+ s_name = data_utils.rand_name('securitygroup-')
+ # Create Security Group with empty string as description
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_security_group, s_name, "")
+ # Create Security Group with white space in description
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_security_group, s_name, " ")
+ # Create Security Group with group description longer than 255 chars
+ s_description = 'description-'.ljust(260, '0')
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_security_group, s_name,
+ s_description)
+
+ @testtools.skipIf(CONF.service_available.neutron,
+ "Neutron allows duplicate names for security groups")
+ @test.attr(type=['negative', 'gate'])
+ def test_security_group_create_with_duplicate_name(self):
+ # Negative test:Security Group with duplicate name should not
+ # be created
+ s_name = data_utils.rand_name('securitygroup-')
+ s_description = data_utils.rand_name('description-')
+ resp, security_group =\
+ self.client.create_security_group(s_name, s_description)
+ self.assertEqual(200, resp.status)
+
+ self.addCleanup(self.client.delete_security_group,
+ security_group['id'])
+ # Now try the Security Group with the same 'Name'
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_security_group, s_name,
+ s_description)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_delete_the_default_security_group(self):
+ # Negative test:Deletion of the "default" Security Group should Fail
+ default_security_group_id = None
+ resp, body = self.client.list_security_groups()
+ for i in range(len(body)):
+ if body[i]['name'] == 'default':
+ default_security_group_id = body[i]['id']
+ break
+ # Deleting the "default" Security Group
+ self.assertRaises(exceptions.BadRequest,
+ self.client.delete_security_group,
+ default_security_group_id)
+
+ @test.attr(type=['negative', 'smoke'])
+ def test_delete_nonexistent_security_group(self):
+ # Negative test:Deletion of a non-existent Security Group should fail
+ non_exist_id = self._generate_a_non_existent_security_group_id()
+ self.assertRaises(exceptions.NotFound,
+ self.client.delete_security_group, non_exist_id)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_delete_security_group_without_passing_id(self):
+ # Negative test:Deletion of a Security Group with out passing ID
+ # should Fail
+ self.assertRaises(exceptions.NotFound,
+ self.client.delete_security_group, '')
+
+ @testtools.skipIf(CONF.service_available.neutron,
+ "Neutron not check the security_group_id")
+ @test.attr(type=['negative', 'gate'])
+ def test_update_security_group_with_invalid_sg_id(self):
+ # Update security_group with invalid sg_id should fail
+ s_name = data_utils.rand_name('sg-')
+ s_description = data_utils.rand_name('description-')
+ # Create a non int sg_id
+ sg_id_invalid = data_utils.rand_name('sg-')
+ self.assertRaises(exceptions.BadRequest,
+ self.client.update_security_group, sg_id_invalid,
+ name=s_name, description=s_description)
+
+ @testtools.skipIf(CONF.service_available.neutron,
+ "Neutron not check the security_group_name")
+ @test.attr(type=['negative', 'gate'])
+ def test_update_security_group_with_invalid_sg_name(self):
+ # Update security_group with invalid sg_name should fail
+ s_name = data_utils.rand_name('sg-')
+ s_description = data_utils.rand_name('description-')
+ resp, securitygroup = \
+ self.client.create_security_group(s_name, s_description)
+ self.assertEqual(200, resp.status)
+ self.assertIn('id', securitygroup)
+ securitygroup_id = securitygroup['id']
+ self.addCleanup(self._delete_security_group,
+ securitygroup_id)
+ # Update Security Group with group name longer than 255 chars
+ s_new_name = 'securitygroup-'.ljust(260, '0')
+ self.assertRaises(exceptions.BadRequest,
+ self.client.update_security_group,
+ securitygroup_id, name=s_new_name)
+
+ @testtools.skipIf(CONF.service_available.neutron,
+ "Neutron not check the security_group_description")
+ @test.attr(type=['negative', 'gate'])
+ def test_update_security_group_with_invalid_sg_des(self):
+ # Update security_group with invalid sg_des should fail
+ s_name = data_utils.rand_name('sg-')
+ s_description = data_utils.rand_name('description-')
+ resp, securitygroup = \
+ self.client.create_security_group(s_name, s_description)
+ self.assertEqual(200, resp.status)
+ self.assertIn('id', securitygroup)
+ securitygroup_id = securitygroup['id']
+ self.addCleanup(self._delete_security_group,
+ securitygroup_id)
+ # Update Security Group with group description longer than 255 chars
+ s_new_des = 'des-'.ljust(260, '0')
+ self.assertRaises(exceptions.BadRequest,
+ self.client.update_security_group,
+ securitygroup_id, description=s_new_des)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_update_non_existent_security_group(self):
+ # Update a non-existent Security Group should Fail
+ non_exist_id = self._generate_a_non_existent_security_group_id()
+ s_name = data_utils.rand_name('sg-')
+ s_description = data_utils.rand_name('description-')
+ self.assertRaises(exceptions.NotFound,
+ self.client.update_security_group,
+ non_exist_id, name=s_name,
+ description=s_description)
+
+
+class SecurityGroupsNegativeTestXML(SecurityGroupsNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index 7ca8a52..1e55afb 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -16,34 +16,20 @@
# under the License.
from tempest.api.compute import base
-from tempest import exceptions
-from tempest.test import attr
+from tempest import test
-class ServerAddressesTest(base.BaseV2ComputeTest):
+class ServerAddressesTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
@classmethod
def setUpClass(cls):
- super(ServerAddressesTest, cls).setUpClass()
+ super(ServerAddressesTestJSON, cls).setUpClass()
cls.client = cls.servers_client
resp, cls.server = cls.create_test_server(wait_until='ACTIVE')
- @attr(type=['negative', 'gate'])
- def test_list_server_addresses_invalid_server_id(self):
- # List addresses request should fail if server id not in system
- self.assertRaises(exceptions.NotFound, self.client.list_addresses,
- '999')
-
- @attr(type=['negative', 'gate'])
- def test_list_server_addresses_by_network_neg(self):
- # List addresses by network should fail if network name not valid
- self.assertRaises(exceptions.NotFound,
- self.client.list_addresses_by_network,
- self.server['id'], 'invalid')
-
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_server_addresses(self):
# All public and private addresses for
# a server should be returned
@@ -60,7 +46,7 @@
self.assertTrue(address['addr'])
self.assertTrue(address['version'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_server_addresses_by_network(self):
# Providing a network type should filter
# the addresses return by that type
@@ -80,5 +66,5 @@
self.assertTrue(any([a for a in addr if a == address]))
-class ServerAddressesTestXML(ServerAddressesTest):
+class ServerAddressesTestXML(ServerAddressesTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/servers/test_server_addresses_negative.py b/tempest/api/compute/servers/test_server_addresses_negative.py
new file mode 100644
index 0000000..30aa7d1
--- /dev/null
+++ b/tempest/api/compute/servers/test_server_addresses_negative.py
@@ -0,0 +1,48 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest import exceptions
+from tempest import test
+
+
+class ServerAddressesNegativeTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServerAddressesNegativeTestJSON, cls).setUpClass()
+ cls.client = cls.servers_client
+
+ resp, cls.server = cls.create_test_server(wait_until='ACTIVE')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_list_server_addresses_invalid_server_id(self):
+ # List addresses request should fail if server id not in system
+ self.assertRaises(exceptions.NotFound, self.client.list_addresses,
+ '999')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_list_server_addresses_by_network_neg(self):
+ # List addresses by network should fail if network name not valid
+ self.assertRaises(exceptions.NotFound,
+ self.client.list_addresses_by_network,
+ self.server['id'], 'invalid')
+
+
+class ServerAddressesNegativeTestXML(ServerAddressesNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/test_auth_token.py b/tempest/api/compute/test_auth_token.py
deleted file mode 100644
index e52c415..0000000
--- a/tempest/api/compute/test_auth_token.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2013 IBM Corp
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-import testtools
-
-from tempest.api.compute import base
-import tempest.config as config
-
-CONF = config.CONF
-
-
-class AuthTokenTestJSON(base.BaseV2ComputeTest):
- _interface = 'json'
-
- @classmethod
- def setUpClass(cls):
- super(AuthTokenTestJSON, cls).setUpClass()
-
- cls.servers_v2 = cls.os.servers_client
- cls.servers_v3 = cls.os.servers_client_v3_auth
-
- def test_v2_token(self):
- # Can get a token using v2 of the identity API and use that to perform
- # an operation on the compute service.
-
- # Doesn't matter which compute API is used,
- # picking list_servers because it's easy.
- self.servers_v2.list_servers()
-
- @testtools.skipIf(not CONF.identity.uri_v3,
- 'v3 auth client not configured')
- def test_v3_token(self):
- # Can get a token using v3 of the identity API and use that to perform
- # an operation on the compute service.
-
- # Doesn't matter which compute API is used,
- # picking list_servers because it's easy.
- self.servers_v3.list_servers()
-
-
-class AuthTokenTestXML(AuthTokenTestJSON):
- _interface = 'xml'
diff --git a/tempest/api/network/base_routers.py b/tempest/api/network/base_routers.py
new file mode 100644
index 0000000..9baef04
--- /dev/null
+++ b/tempest/api/network/base_routers.py
@@ -0,0 +1,50 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.network import base
+
+
+class BaseRouterTest(base.BaseAdminNetworkTest):
+ # NOTE(salv-orlando): This class inherits from BaseAdminNetworkTest
+ # as some router operations, such as enabling or disabling SNAT
+ # require admin credentials by default
+
+ @classmethod
+ def setUpClass(cls):
+ super(BaseRouterTest, cls).setUpClass()
+
+ def _delete_router(self, router_id):
+ resp, _ = self.client.delete_router(router_id)
+ self.assertEqual(204, resp.status)
+ # Asserting that the router is not found in the list
+ # after deletion
+ resp, list_body = self.client.list_routers()
+ self.assertEqual('200', resp['status'])
+ routers_list = list()
+ for router in list_body['routers']:
+ routers_list.append(router['id'])
+ self.assertNotIn(router_id, routers_list)
+
+ def _remove_router_interface_with_subnet_id(self, router_id, subnet_id):
+ resp, _ = self.client.remove_router_interface_with_subnet_id(
+ router_id, subnet_id)
+ self.assertEqual('200', resp['status'])
+
+ def _remove_router_interface_with_port_id(self, router_id, port_id):
+ resp, _ = self.client.remove_router_interface_with_port_id(
+ router_id, port_id)
+ self.assertEqual('200', resp['status'])
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 21934f2..b6022e8 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -17,43 +17,18 @@
import netaddr
-from tempest.api.network import base
+from tempest.api.network import base_routers as base
from tempest.common.utils import data_utils
from tempest import test
-class RoutersTest(base.BaseAdminNetworkTest):
- # NOTE(salv-orlando): This class inherits from BaseAdminNetworkTest
- # as some router operations, such as enabling or disabling SNAT
- # require admin credentials by default
+class RoutersTest(base.BaseRouterTest):
_interface = 'json'
@classmethod
def setUpClass(cls):
super(RoutersTest, cls).setUpClass()
- def _delete_router(self, router_id):
- resp, _ = self.client.delete_router(router_id)
- self.assertEqual(204, resp.status)
- # Asserting that the router is not found in the list
- # after deletion
- resp, list_body = self.client.list_routers()
- self.assertEqual('200', resp['status'])
- routers_list = list()
- for router in list_body['routers']:
- routers_list.append(router['id'])
- self.assertNotIn(router_id, routers_list)
-
- def _remove_router_interface_with_subnet_id(self, router_id, subnet_id):
- resp, _ = self.client.remove_router_interface_with_subnet_id(
- router_id, subnet_id)
- self.assertEqual('200', resp['status'])
-
- def _remove_router_interface_with_port_id(self, router_id, port_id):
- resp, _ = self.client.remove_router_interface_with_port_id(
- router_id, port_id)
- self.assertEqual('200', resp['status'])
-
@test.attr(type='smoke')
def test_create_show_list_update_delete_router(self):
# Create a router
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
new file mode 100644
index 0000000..520a612
--- /dev/null
+++ b/tempest/api/network/test_routers_negative.py
@@ -0,0 +1,57 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.network import base_routers as base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class RoutersNegativeTest(base.BaseRouterTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(RoutersNegativeTest, cls).setUpClass()
+ cls.router = cls.create_router(data_utils.rand_name('router-'))
+ cls.network = cls.create_network()
+ cls.subnet = cls.create_subnet(cls.network)
+
+ @attr(type=['negative', 'smoke'])
+ def test_router_add_gateway_invalid_network_returns_404(self):
+ self.assertRaises(exceptions.NotFound,
+ self.client.update_router,
+ self.router['id'],
+ external_gateway_info={
+ 'network_id': self.router['id']})
+
+ @attr(type=['negative', 'smoke'])
+ def test_router_add_gateway_net_not_external_returns_400(self):
+ self.create_subnet(self.network)
+ self.assertRaises(exceptions.BadRequest,
+ self.client.update_router,
+ self.router['id'],
+ external_gateway_info={
+ 'network_id': self.network['id']})
+
+ @attr(type=['negative', 'smoke'])
+ def test_router_remove_interface_in_use_returns_409(self):
+ self.client.add_router_interface_with_subnet_id(
+ self.router['id'], self.subnet['id'])
+ self.assertRaises(exceptions.Conflict,
+ self.client.delete_router,
+ self.router['id'])
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index ec8b3a1..547d0d0 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -86,6 +86,12 @@
return self.cmd_with_auth(
'ceilometer', action, flags, params, admin, fail_ok)
+ def heat(self, action, flags='', params='', admin=True,
+ fail_ok=False):
+ """Executes heat command for the given action."""
+ return self.cmd_with_auth(
+ 'heat', action, flags, params, admin, fail_ok)
+
def cinder(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes cinder command for the given action."""
return self.cmd_with_auth(
diff --git a/tempest/cli/simple_read_only/heat_templates/heat_minimal.yaml b/tempest/cli/simple_read_only/heat_templates/heat_minimal.yaml
new file mode 100644
index 0000000..7dcda39
--- /dev/null
+++ b/tempest/cli/simple_read_only/heat_templates/heat_minimal.yaml
@@ -0,0 +1,18 @@
+HeatTemplateFormatVersion: '2012-12-12'
+Description: Minimal template to test validation
+Parameters:
+ InstanceImage:
+ Description: Glance image name
+ Type: String
+ InstanceType:
+ Description: Nova instance type
+ Type: String
+ Default: m1.small
+ AllowedValues: [m1.tiny, m1.small, m1.medium, m1.large, m1.nano, m1.xlarge, m1.micro]
+ ConstraintDescription: must be a valid nova instance type.
+Resources:
+ InstanceResource:
+ Type: OS::Nova::Server
+ Properties:
+ flavor: {Ref: InstanceType}
+ image: {Ref: InstanceImage}
diff --git a/tempest/cli/simple_read_only/heat_templates/heat_minimal_hot.yaml b/tempest/cli/simple_read_only/heat_templates/heat_minimal_hot.yaml
new file mode 100644
index 0000000..6d89b7b
--- /dev/null
+++ b/tempest/cli/simple_read_only/heat_templates/heat_minimal_hot.yaml
@@ -0,0 +1,19 @@
+heat_template_version: 2013-05-23
+description: A minimal HOT test template
+parameters:
+ instance_image:
+ description: Glance image name
+ type: String
+ instance_type:
+ description: Nova instance type
+ type: String
+ default: m1.small
+ constraints:
+ - allowed_values: [m1.small, m1.medium, m1.large]
+ description: instance_type must be one of m1.small, m1.medium or m1.large
+resources:
+ instance:
+ type: OS::Nova::Server
+ properties:
+ image: { get_param: instance_image }
+ flavor: { get_param: instance_type }
diff --git a/tempest/cli/simple_read_only/test_heat.py b/tempest/cli/simple_read_only/test_heat.py
new file mode 100644
index 0000000..e2fefe8
--- /dev/null
+++ b/tempest/cli/simple_read_only/test_heat.py
@@ -0,0 +1,98 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+import os
+import yaml
+
+from oslo.config import cfg
+
+import tempest.cli
+from tempest.openstack.common import log as logging
+
+CONF = cfg.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class SimpleReadOnlyHeatClientTest(tempest.cli.ClientTestBase):
+ """Basic, read-only tests for Heat CLI client.
+
+ Basic smoke test for the heat CLI commands which do not require
+ creating or modifying stacks.
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ if (not CONF.service_available.heat):
+ msg = ("Skipping all Heat cli tests because it is "
+ "not available")
+ raise cls.skipException(msg)
+ super(SimpleReadOnlyHeatClientTest, cls).setUpClass()
+
+ def test_heat_stack_list(self):
+ self.heat('stack-list')
+
+ def test_heat_stack_list_debug(self):
+ self.heat('stack-list', flags='--debug')
+
+ def test_heat_resource_template_fmt_default(self):
+ ret = self.heat('resource-template OS::Nova::Server')
+ self.assertIn('Type: OS::Nova::Server', ret)
+
+ def test_heat_resource_template_fmt_arg_short_yaml(self):
+ ret = self.heat('resource-template -F yaml OS::Nova::Server')
+ self.assertIn('Type: OS::Nova::Server', ret)
+ self.assertIsInstance(yaml.safe_load(ret), dict)
+
+ def test_heat_resource_template_fmt_arg_long_json(self):
+ ret = self.heat('resource-template --format json OS::Nova::Server')
+ self.assertIn('"Type": "OS::Nova::Server",', ret)
+ self.assertIsInstance(json.loads(ret), dict)
+
+ def test_heat_resource_type_list(self):
+ ret = self.heat('resource-type-list')
+ rsrc_types = self.parser.listing(ret)
+ self.assertTableStruct(rsrc_types, ['resource_type'])
+
+ def test_heat_resource_type_show(self):
+ rsrc_schema = self.heat('resource-type-show OS::Nova::Server')
+ # resource-type-show returns a json resource schema
+ self.assertIsInstance(json.loads(rsrc_schema), dict)
+
+ def test_heat_template_validate_yaml(self):
+ filepath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ 'heat_templates/heat_minimal.yaml')
+ ret = self.heat('template-validate -f %s' % filepath)
+ # On success template-validate returns a json representation
+ # of the template parameters
+ self.assertIsInstance(json.loads(ret), dict)
+
+ def test_heat_template_validate_hot(self):
+ filepath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ 'heat_templates/heat_minimal_hot.yaml')
+ ret = self.heat('template-validate -f %s' % filepath)
+ self.assertIsInstance(json.loads(ret), dict)
+
+ def test_heat_help(self):
+ self.heat('help')
+
+ def test_heat_help_cmd(self):
+ # Check requesting help for a specific command works
+ help_text = self.heat('help resource-template')
+ lines = help_text.split('\n')
+ self.assertFirstLineStartsWith(lines, 'usage: heat resource-template')
+
+ def test_heat_version(self):
+ self.heat('', flags='--version')
diff --git a/tempest/services/compute/json/security_groups_client.py b/tempest/services/compute/json/security_groups_client.py
index 1f01437..361ec36 100644
--- a/tempest/services/compute/json/security_groups_client.py
+++ b/tempest/services/compute/json/security_groups_client.py
@@ -63,6 +63,25 @@
body = json.loads(body)
return resp, body['security_group']
+ def update_security_group(self, security_group_id, name=None,
+ description=None):
+ """
+ Update a security group.
+ security_group_id: a security_group to update
+ name: new name of security group
+ description: new description of security group
+ """
+ post_body = {}
+ if name:
+ post_body['name'] = name
+ if description:
+ post_body['description'] = description
+ post_body = json.dumps({'security_group': post_body})
+ resp, body = self.put('os-security-groups/%s' % str(security_group_id),
+ post_body, self.headers)
+ body = json.loads(body)
+ return resp, body['security_group']
+
def delete_security_group(self, security_group_id):
"""Deletes the provided Security Group."""
return self.delete('os-security-groups/%s' % str(security_group_id))
diff --git a/tempest/services/compute/xml/security_groups_client.py b/tempest/services/compute/xml/security_groups_client.py
index 5d86790..aebeb4d 100644
--- a/tempest/services/compute/xml/security_groups_client.py
+++ b/tempest/services/compute/xml/security_groups_client.py
@@ -79,6 +79,30 @@
body = self._parse_body(etree.fromstring(body))
return resp, body
+ def update_security_group(self, security_group_id, name=None,
+ description=None):
+ """
+ Update a security group.
+ security_group_id: a security_group to update
+ name: new name of security group
+ description: new description of security group
+ """
+ security_group = Element("security_group")
+ if name:
+ sg_name = Element("name")
+ sg_name.append(Text(content=name))
+ security_group.append(sg_name)
+ if description:
+ des = Element("description")
+ des.append(Text(content=description))
+ security_group.append(des)
+ resp, body = self.put('os-security-groups/%s' %
+ str(security_group_id),
+ str(Document(security_group)),
+ self.headers)
+ body = self._parse_body(etree.fromstring(body))
+ return resp, body
+
def delete_security_group(self, security_group_id):
"""Deletes the provided Security Group."""
return self.delete('os-security-groups/%s' %
diff --git a/tox.ini b/tox.ini
index b44b3e0..6d596e3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,13 +4,14 @@
skipsdist = True
[testenv]
+sitepackages = True
setenv = VIRTUAL_ENV={envdir}
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_ALL=C
OS_TEST_PATH=./tempest/test_discover
usedevelop = True
-install_command = pip install -U {opts} {packages}
+install_command = pip install {opts} {packages}
[testenv:py26]
setenv = OS_TEST_PATH=./tempest/tests
@@ -25,38 +26,32 @@
commands = python setup.py test --slowest --testr-arg='tempest\.tests {posargs}'
[testenv:all]
-sitepackages = True
setenv = VIRTUAL_ENV={envdir}
commands =
python setup.py testr --slowest --testr-args='{posargs}'
[testenv:full]
-sitepackages = True
# The regex below is used to select which tests to run and exclude the slow tag:
# See the testrepostiory bug: https://bugs.launchpad.net/testrepository/+bug/1208610
commands =
bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
[testenv:testr-full]
-sitepackages = True
commands =
bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
[testenv:heat-slow]
-sitepackages = True
setenv = OS_TEST_TIMEOUT=1200
# The regex below is used to select heat api/scenario tests tagged as slow.
commands =
bash tools/pretty_tox_serial.sh '(?=.*\[.*\bslow\b.*\])(^tempest\.(api|scenario)\.orchestration) {posargs}'
[testenv:large-ops]
-sitepackages = True
commands =
python setup.py testr --slowest --testr-args='tempest.scenario.test_large_ops {posargs}'
[testenv:py26-full]
-sitepackages = True
setenv = VIRTUAL_ENV={envdir}
NOSE_WITH_OPENSTACK=1
NOSE_OPENSTACK_COLOR=1
@@ -81,12 +76,10 @@
nosetests --logging-format '%(asctime)-15s %(message)s' --with-xunit -sv --attr=type=smoke --xunit-file=nosetests-smoke.xml tempest {posargs}
[testenv:smoke]
-sitepackages = True
commands =
bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])((smoke)|(^tempest\.scenario)) {posargs}'
[testenv:smoke-serial]
-sitepackages = True
# This is still serial because neutron doesn't work with parallel. See:
# https://bugs.launchpad.net/tempest/+bug/1216076 so the neutron smoke
# job would fail if we moved it to parallel.
@@ -94,7 +87,6 @@
bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])((smoke)|(^tempest\.scenario)) {posargs}'
[testenv:stress]
-sitepackages = True
commands =
python -m tempest/stress/run_stress -a -d 3600 -S