Merge "Remove Resp status code checks in stress tests"
diff --git a/doc/source/cleanup.rst b/doc/source/cleanup.rst
new file mode 100644
index 0000000..acd016c
--- /dev/null
+++ b/doc/source/cleanup.rst
@@ -0,0 +1,5 @@
+--------------------------------
+Post Tempest Run Cleanup Utility
+--------------------------------
+
+.. automodule:: tempest.cmd.cleanup
\ No newline at end of file
diff --git a/doc/source/index.rst b/doc/source/index.rst
index d3118ac..bc4fc46 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -29,6 +29,15 @@
field_guide/thirdparty
field_guide/unit_tests
+---------------------
+Command Documentation
+---------------------
+
+.. toctree::
+ :maxdepth: 1
+
+ cleanup
+
==================
Indices and tables
==================
diff --git a/setup.cfg b/setup.cfg
index 5c62710..2e25ace 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -22,6 +22,7 @@
verify-tempest-config = tempest.cmd.verify_tempest_config:main
javelin2 = tempest.cmd.javelin:main
run-tempest-stress = tempest.cmd.run_stress:main
+ tempest-cleanup = tempest.cmd.cleanup:main
[build_sphinx]
all_files = 1
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 834c010..d9b2848 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -83,6 +83,7 @@
cls.fw_rules = []
cls.fw_policies = []
cls.ipsecpolicies = []
+ cls.ethertype = "IPv" + str(cls._ip_version)
@classmethod
def resource_cleanup(cls):
diff --git a/tempest/api/network/test_security_groups.py b/tempest/api/network/test_security_groups.py
index 9764b4d..58ad39c 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -17,11 +17,15 @@
from tempest.api.network import base_security_groups as base
from tempest.common.utils import data_utils
+from tempest import config
from tempest import test
+CONF = config.CONF
+
class SecGroupTest(base.BaseSecGroupTest):
_interface = 'json'
+ _tenant_network_cidr = CONF.network.tenant_network_cidr
@classmethod
def resource_setup(cls):
@@ -30,6 +34,40 @@
msg = "security-group extension not enabled."
raise cls.skipException(msg)
+ def _create_verify_security_group_rule(self, sg_id, direction,
+ ethertype, protocol,
+ port_range_min,
+ port_range_max,
+ remote_group_id=None,
+ remote_ip_prefix=None):
+ # Create Security Group rule with the input params and validate
+ # that SG rule is created with the same parameters.
+ resp, rule_create_body = self.client.create_security_group_rule(
+ security_group_id=sg_id,
+ direction=direction,
+ ethertype=ethertype,
+ protocol=protocol,
+ port_range_min=port_range_min,
+ port_range_max=port_range_max,
+ remote_group_id=remote_group_id,
+ remote_ip_prefix=remote_ip_prefix
+ )
+
+ sec_group_rule = rule_create_body['security_group_rule']
+ self.addCleanup(self._delete_security_group_rule,
+ sec_group_rule['id'])
+
+ expected = {'direction': direction, 'protocol': protocol,
+ 'ethertype': ethertype, 'port_range_min': port_range_min,
+ 'port_range_max': port_range_max,
+ 'remote_group_id': remote_group_id,
+ 'remote_ip_prefix': remote_ip_prefix}
+ for key, value in six.iteritems(expected):
+ self.assertEqual(value, sec_group_rule[key],
+ "Field %s of the created security group "
+ "rule does not match with %s." %
+ (key, value))
+
@test.attr(type='smoke')
def test_list_security_groups(self):
# Verify the that security group belonging to tenant exist in list
@@ -80,7 +118,8 @@
_, rule_create_body = self.client.create_security_group_rule(
security_group_id=group_create_body['security_group']['id'],
protocol=protocol,
- direction='ingress'
+ direction='ingress',
+ ethertype=self.ethertype
)
# Show details of the created security rule
@@ -102,30 +141,93 @@
@test.attr(type='smoke')
def test_create_security_group_rule_with_additional_args(self):
- # Verify creating security group rule with the following
- # arguments works: "protocol": "tcp", "port_range_max": 77,
- # "port_range_min": 77, "direction":"ingress".
- group_create_body, _ = self._create_security_group()
+ """Verify security group rule with additional arguments works.
+ direction:ingress, ethertype:[IPv4/IPv6],
+ protocol:tcp, port_range_min:77, port_range_max:77
+ """
+ group_create_body, _ = self._create_security_group()
+ sg_id = group_create_body['security_group']['id']
direction = 'ingress'
protocol = 'tcp'
port_range_min = 77
port_range_max = 77
- _, rule_create_body = self.client.create_security_group_rule(
- security_group_id=group_create_body['security_group']['id'],
- direction=direction,
- protocol=protocol,
- port_range_min=port_range_min,
- port_range_max=port_range_max
- )
+ self._create_verify_security_group_rule(sg_id, direction,
+ self.ethertype, protocol,
+ port_range_min,
+ port_range_max)
- sec_group_rule = rule_create_body['security_group_rule']
+ @test.attr(type='smoke')
+ def test_create_security_group_rule_with_icmp_type_code(self):
+ """Verify security group rule for icmp protocol works.
- self.assertEqual(sec_group_rule['direction'], direction)
- self.assertEqual(sec_group_rule['protocol'], protocol)
- self.assertEqual(int(sec_group_rule['port_range_min']), port_range_min)
- self.assertEqual(int(sec_group_rule['port_range_max']), port_range_max)
+ Specify icmp type (port_range_min) and icmp code
+ (port_range_max) with different values. A seperate testcase
+ is added for icmp protocol as icmp validation would be
+ different from tcp/udp.
+ """
+ group_create_body, _ = self._create_security_group()
+
+ sg_id = group_create_body['security_group']['id']
+ direction = 'ingress'
+ protocol = 'icmp'
+ icmp_type_codes = [(3, 2), (2, 3), (3, 0), (2, None)]
+ for icmp_type, icmp_code in icmp_type_codes:
+ self._create_verify_security_group_rule(sg_id, direction,
+ self.ethertype, protocol,
+ icmp_type, icmp_code)
+
+ @test.attr(type='smoke')
+ def test_create_security_group_rule_with_remote_group_id(self):
+ # Verify creating security group rule with remote_group_id works
+ sg1_body, _ = self._create_security_group()
+ sg2_body, _ = self._create_security_group()
+
+ sg_id = sg1_body['security_group']['id']
+ direction = 'ingress'
+ protocol = 'udp'
+ port_range_min = 50
+ port_range_max = 55
+ remote_id = sg2_body['security_group']['id']
+ self._create_verify_security_group_rule(sg_id, direction,
+ self.ethertype, protocol,
+ port_range_min,
+ port_range_max,
+ remote_group_id=remote_id)
+
+ @test.attr(type='smoke')
+ def test_create_security_group_rule_with_remote_ip_prefix(self):
+ # Verify creating security group rule with remote_ip_prefix works
+ sg1_body, _ = self._create_security_group()
+
+ sg_id = sg1_body['security_group']['id']
+ direction = 'ingress'
+ protocol = 'tcp'
+ port_range_min = 76
+ port_range_max = 77
+ ip_prefix = self._tenant_network_cidr
+ self._create_verify_security_group_rule(sg_id, direction,
+ self.ethertype, protocol,
+ port_range_min,
+ port_range_max,
+ remote_ip_prefix=ip_prefix)
class SecGroupTestXML(SecGroupTest):
_interface = 'xml'
+
+
+class SecGroupIPv6Test(SecGroupTest):
+ _ip_version = 6
+ _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+
+ @classmethod
+ def setUpClass(cls):
+ if not CONF.network_feature_enabled.ipv6:
+ skip_msg = "IPv6 Tests are disabled."
+ raise cls.skipException(skip_msg)
+ super(SecGroupIPv6Test, cls).setUpClass()
+
+
+class SecGroupIPv6TestXML(SecGroupIPv6Test):
+ _interface = 'xml'
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index 9c6c267..2e3091e 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -16,12 +16,16 @@
import uuid
from tempest.api.network import base_security_groups as base
+from tempest import config
from tempest import exceptions
from tempest import test
+CONF = config.CONF
+
class NegativeSecGroupTest(base.BaseSecGroupTest):
_interface = 'json'
+ _tenant_network_cidr = CONF.network.tenant_network_cidr
@classmethod
def resource_setup(cls):
@@ -60,23 +64,87 @@
self.assertRaises(
exceptions.BadRequest, self.client.create_security_group_rule,
security_group_id=group_create_body['security_group']['id'],
- protocol=pname, direction='ingress')
+ protocol=pname, direction='ingress', ethertype=self.ethertype)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_security_group_rule_with_bad_remote_ip_prefix(self):
+ group_create_body, _ = self._create_security_group()
+
+ # Create rule with bad remote_ip_prefix
+ prefix = ['192.168.1./24', '192.168.1.1/33', 'bad_prefix', '256']
+ for remote_ip_prefix in prefix:
+ self.assertRaises(
+ exceptions.BadRequest, self.client.create_security_group_rule,
+ security_group_id=group_create_body['security_group']['id'],
+ protocol='tcp', direction='ingress', ethertype=self.ethertype,
+ remote_ip_prefix=remote_ip_prefix)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_security_group_rule_with_non_existent_remote_groupid(self):
+ group_create_body, _ = self._create_security_group()
+ non_exist_id = str(uuid.uuid4())
+
+ # Create rule with non existent remote_group_id
+ group_ids = ['bad_group_id', non_exist_id]
+ for remote_group_id in group_ids:
+ self.assertRaises(
+ exceptions.NotFound, self.client.create_security_group_rule,
+ security_group_id=group_create_body['security_group']['id'],
+ protocol='tcp', direction='ingress', ethertype=self.ethertype,
+ remote_group_id=remote_group_id)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_security_group_rule_with_remote_ip_and_group(self):
+ sg1_body, _ = self._create_security_group()
+ sg2_body, _ = self._create_security_group()
+
+ # Create rule specifying both remote_ip_prefix and remote_group_id
+ prefix = self._tenant_network_cidr
+ self.assertRaises(
+ exceptions.BadRequest, self.client.create_security_group_rule,
+ security_group_id=sg1_body['security_group']['id'],
+ protocol='tcp', direction='ingress',
+ ethertype=self.ethertype, remote_ip_prefix=prefix,
+ remote_group_id=sg2_body['security_group']['id'])
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_security_group_rule_with_bad_ethertype(self):
+ group_create_body, _ = self._create_security_group()
+
+ # Create rule with bad ethertype
+ ethertype = 'bad_ethertype'
+ self.assertRaises(
+ exceptions.BadRequest, self.client.create_security_group_rule,
+ security_group_id=group_create_body['security_group']['id'],
+ protocol='udp', direction='ingress', ethertype=ethertype)
@test.attr(type=['negative', 'gate'])
def test_create_security_group_rule_with_invalid_ports(self):
group_create_body, _ = self._create_security_group()
- # Create rule with invalid ports
+ # Create rule for tcp protocol with invalid ports
states = [(-16, 80, 'Invalid value for port -16'),
(80, 79, 'port_range_min must be <= port_range_max'),
(80, 65536, 'Invalid value for port 65536'),
+ (None, 6, 'port_range_min must be <= port_range_max'),
(-16, 65536, 'Invalid value for port')]
for pmin, pmax, msg in states:
ex = self.assertRaises(
exceptions.BadRequest, self.client.create_security_group_rule,
security_group_id=group_create_body['security_group']['id'],
protocol='tcp', port_range_min=pmin, port_range_max=pmax,
- direction='ingress')
+ direction='ingress', ethertype=self.ethertype)
+ self.assertIn(msg, str(ex))
+
+ # Create rule for icmp protocol with invalid ports
+ states = [(1, 256, 'Invalid value for ICMP code'),
+ (300, 1, 'Invalid value for ICMP type')]
+ for pmin, pmax, msg in states:
+ ex = self.assertRaises(
+ exceptions.BadRequest, self.client.create_security_group_rule,
+ security_group_id=group_create_body['security_group']['id'],
+ protocol='icmp', port_range_min=pmin, port_range_max=pmax,
+ direction='ingress', ethertype=self.ethertype)
self.assertIn(msg, str(ex))
@test.attr(type=['negative', 'smoke'])
@@ -88,14 +156,54 @@
name=name)
@test.attr(type=['negative', 'smoke'])
+ def test_create_duplicate_security_group_rule_fails(self):
+ # Create duplicate security group rule, it should fail.
+ body, _ = self._create_security_group()
+
+ min_port = 66
+ max_port = 67
+ # Create a rule with valid params
+ resp, _ = self.client.create_security_group_rule(
+ security_group_id=body['security_group']['id'],
+ direction='ingress',
+ ethertype=self.ethertype,
+ protocol='tcp',
+ port_range_min=min_port,
+ port_range_max=max_port
+ )
+
+ # Try creating the same security group rule, it should fail
+ self.assertRaises(
+ exceptions.Conflict, self.client.create_security_group_rule,
+ security_group_id=body['security_group']['id'],
+ protocol='tcp', direction='ingress', ethertype=self.ethertype,
+ port_range_min=min_port, port_range_max=max_port)
+
+ @test.attr(type=['negative', 'smoke'])
def test_create_security_group_rule_with_non_existent_security_group(self):
# Create security group rules with not existing security group.
non_existent_sg = str(uuid.uuid4())
self.assertRaises(exceptions.NotFound,
self.client.create_security_group_rule,
security_group_id=non_existent_sg,
- direction='ingress')
+ direction='ingress', ethertype=self.ethertype)
class NegativeSecGroupTestXML(NegativeSecGroupTest):
_interface = 'xml'
+
+
+class NegativeSecGroupIPv6Test(NegativeSecGroupTest):
+ _ip_version = 6
+ _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+
+ @classmethod
+ def setUpClass(cls):
+ if not CONF.network_feature_enabled.ipv6:
+ skip_msg = "IPv6 Tests are disabled."
+ raise cls.skipException(skip_msg)
+ super(NegativeSecGroupIPv6Test, cls).setUpClass()
+
+
+class NegativeSecGroupIPv6TestXML(NegativeSecGroupIPv6Test):
+ _interface = 'xml'
diff --git a/tempest/api/orchestration/stacks/test_update.py b/tempest/api/orchestration/stacks/test_update.py
deleted file mode 100644
index 98761ac..0000000
--- a/tempest/api/orchestration/stacks/test_update.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# 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 logging
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-LOG = logging.getLogger(__name__)
-
-
-class UpdateStackTestJSON(base.BaseOrchestrationTest):
- _interface = 'json'
-
- template = '''
-heat_template_version: 2013-05-23
-resources:
- random1:
- type: OS::Heat::RandomString
-'''
- update_template = '''
-heat_template_version: 2013-05-23
-resources:
- random1:
- type: OS::Heat::RandomString
- random2:
- type: OS::Heat::RandomString
-'''
-
- def update_stack(self, stack_identifier, template):
- stack_name = stack_identifier.split('/')[0]
- self.client.update_stack(
- stack_identifier=stack_identifier,
- name=stack_name,
- template=template)
- self.client.wait_for_stack_status(stack_identifier, 'UPDATE_COMPLETE')
-
- @test.attr(type='gate')
- def test_stack_update_nochange(self):
- stack_name = data_utils.rand_name('heat')
- stack_identifier = self.create_stack(stack_name, self.template)
- self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
- expected_resources = {'random1': 'OS::Heat::RandomString'}
- self.assertEqual(expected_resources,
- self.list_resources(stack_identifier))
-
- # Update with no changes, resources should be unchanged
- self.update_stack(stack_identifier, self.template)
- self.assertEqual(expected_resources,
- self.list_resources(stack_identifier))
-
- @test.attr(type='gate')
- def test_stack_update_add_remove(self):
- stack_name = data_utils.rand_name('heat')
- stack_identifier = self.create_stack(stack_name, self.template)
- self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
- initial_resources = {'random1': 'OS::Heat::RandomString'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- # Add one resource via a stack update
- self.update_stack(stack_identifier, self.update_template)
- updated_resources = {'random1': 'OS::Heat::RandomString',
- 'random2': 'OS::Heat::RandomString'}
- self.assertEqual(updated_resources,
- self.list_resources(stack_identifier))
-
- # Then remove it by updating with the original template
- self.update_stack(stack_identifier, self.template)
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 769f5e0..db2aab5 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -25,9 +25,8 @@
_interface = "json"
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumeMultiBackendTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumeMultiBackendTest, cls).resource_setup()
if not CONF.volume_feature_enabled.multi_backend:
raise cls.skipException("Cinder multi-backend feature disabled")
@@ -76,7 +75,7 @@
self.volume['id'], 'available')
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
# volumes deletion
vid_prefix = getattr(cls, 'volume_id_list_with_prefix', [])
for volume_id in vid_prefix:
@@ -93,7 +92,7 @@
for volume_type_id in volume_type_id_list:
cls.client.delete_volume_type(volume_type_id)
- super(VolumeMultiBackendTest, cls).tearDownClass()
+ super(VolumeMultiBackendTest, cls).resource_cleanup()
@test.attr(type='smoke')
def test_backend_name_reporting(self):
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index abbe1e9..720734b 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -22,9 +22,8 @@
_interface = "json"
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(SnapshotsActionsTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(SnapshotsActionsTest, cls).resource_setup()
cls.client = cls.snapshots_client
# Create admin volume client
@@ -46,7 +45,7 @@
'available')
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
# Delete the test snapshot
cls.client.delete_snapshot(cls.snapshot['id'])
cls.client.wait_for_resource_deletion(cls.snapshot['id'])
@@ -55,7 +54,7 @@
cls.volumes_client.delete_volume(cls.volume['id'])
cls.volumes_client.wait_for_resource_deletion(cls.volume['id'])
- super(SnapshotsActionsTest, cls).tearDownClass()
+ super(SnapshotsActionsTest, cls).resource_cleanup()
def tearDown(self):
# Set snapshot's status to available after test
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index fa3b667..7e24fa4 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -27,8 +27,8 @@
force_tenant_isolation = True
@classmethod
- def setUpClass(cls):
- super(VolumeQuotasAdminTestJSON, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumeQuotasAdminTestJSON, cls).resource_setup()
cls.admin_volume_client = cls.os_adm.volumes_client
cls.demo_tenant_id = cls.isolated_creds.get_primary_creds().tenant_id
diff --git a/tempest/api/volume/admin/test_volume_quotas_negative.py b/tempest/api/volume/admin/test_volume_quotas_negative.py
index 515024f..60a0adb 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -23,9 +23,8 @@
force_tenant_isolation = True
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumeQuotasNegativeTestJSON, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumeQuotasNegativeTestJSON, cls).resource_setup()
demo_user = cls.isolated_creds.get_primary_creds()
cls.demo_tenant_id = demo_user.tenant_id
cls.shared_quota_set = {'gigabytes': 3, 'volumes': 1, 'snapshots': 1}
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index 4a68e05..7820148 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -25,8 +25,8 @@
_interface = "json"
@classmethod
- def setUpClass(cls):
- super(VolumesServicesTestJSON, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesServicesTestJSON, cls).resource_setup()
cls.client = cls.os_adm.volume_services_client
_, cls.services = cls.client.list_services()
cls.host_name = cls.services[0]['host']
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index c682866..2d72dd2 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -22,15 +22,15 @@
_interface = "json"
@classmethod
- def setUpClass(cls):
- super(VolumeTypesExtraSpecsTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumeTypesExtraSpecsTest, cls).resource_setup()
vol_type_name = data_utils.rand_name('Volume-type-')
_, cls.volume_type = cls.client.create_volume_type(vol_type_name)
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
cls.client.delete_volume_type(cls.volume_type['id'])
- super(VolumeTypesExtraSpecsTest, cls).tearDownClass()
+ super(VolumeTypesExtraSpecsTest, cls).resource_cleanup()
@test.attr(type='smoke')
def test_volume_type_extra_specs_list(self):
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 ff4f113..f3eee00 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
@@ -25,8 +25,8 @@
_interface = 'json'
@classmethod
- def setUpClass(cls):
- super(ExtraSpecsNegativeTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(ExtraSpecsNegativeTest, cls).resource_setup()
vol_type_name = data_utils.rand_name('Volume-type-')
cls.extra_specs = {"spec1": "val1"}
_, cls.volume_type = cls.client.create_volume_type(
@@ -34,9 +34,9 @@
extra_specs=cls.extra_specs)
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
cls.client.delete_volume_type(cls.volume_type['id'])
- super(ExtraSpecsNegativeTest, cls).tearDownClass()
+ super(ExtraSpecsNegativeTest, cls).resource_cleanup()
@test.attr(type='gate')
def test_update_no_body(self):
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index d6db1df..f85718b 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -22,9 +22,8 @@
_interface = "json"
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumesActionsTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesActionsTest, cls).resource_setup()
cls.client = cls.volumes_client
# Create admin volume client
@@ -38,12 +37,12 @@
cls.client.wait_for_volume_status(cls.volume['id'], 'available')
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
# Delete the test volume
cls.client.delete_volume(cls.volume['id'])
cls.client.wait_for_resource_deletion(cls.volume['id'])
- super(VolumesActionsTest, cls).tearDownClass()
+ super(VolumesActionsTest, cls).resource_cleanup()
def _reset_volume_status(self, volume_id, status):
# Reset the volume status
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 3699e9c..8b90b07 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -27,9 +27,8 @@
_interface = "json"
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumesBackupsTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesBackupsTest, cls).resource_setup()
if not CONF.volume_feature_enabled.backup:
raise cls.skipException("Cinder backup feature disabled")
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 8aad058..7f5361d 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -32,9 +32,9 @@
_interface = 'json'
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
cls.set_network_resources()
- super(BaseVolumeTest, cls).setUpClass()
+ super(BaseVolumeTest, cls).resource_setup()
if not CONF.service_available.cinder:
skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
@@ -83,11 +83,11 @@
raise exceptions.InvalidConfiguration(message=msg)
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
cls.clear_snapshots()
cls.clear_volumes()
cls.clear_isolated_creds()
- super(BaseVolumeTest, cls).tearDownClass()
+ super(BaseVolumeTest, cls).resource_cleanup()
@classmethod
def create_volume(cls, size=1, **kwargs):
@@ -152,8 +152,8 @@
class BaseVolumeAdminTest(BaseVolumeTest):
"""Base test case class for all Volume Admin API tests."""
@classmethod
- def setUpClass(cls):
- super(BaseVolumeAdminTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(BaseVolumeAdminTest, cls).resource_setup()
cls.adm_user = CONF.identity.admin_username
cls.adm_pass = CONF.identity.admin_password
cls.adm_tenant = CONF.identity.admin_tenant_name
@@ -187,9 +187,9 @@
cls.volume_qos_client = cls.os_adm.volume_qos_v2_client
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
cls.clear_qos_specs()
- super(BaseVolumeAdminTest, cls).tearDownClass()
+ super(BaseVolumeAdminTest, cls).resource_cleanup()
@classmethod
def create_test_qos_specs(cls, name=None, consumer=None, **kwargs):
diff --git a/tempest/api/volume/test_availability_zone.py b/tempest/api/volume/test_availability_zone.py
index c026f71..648bd8b 100644
--- a/tempest/api/volume/test_availability_zone.py
+++ b/tempest/api/volume/test_availability_zone.py
@@ -24,8 +24,8 @@
"""
@classmethod
- def setUpClass(cls):
- super(AvailabilityZoneV2TestJSON, cls).setUpClass()
+ def resource_setup(cls):
+ super(AvailabilityZoneV2TestJSON, cls).resource_setup()
cls.client = cls.availability_zone_client
@test.attr(type='gate')
diff --git a/tempest/api/volume/test_qos.py b/tempest/api/volume/test_qos.py
index 8b6ba49..a719b79 100644
--- a/tempest/api/volume/test_qos.py
+++ b/tempest/api/volume/test_qos.py
@@ -25,9 +25,8 @@
"""
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(QosSpecsV2TestJSON, cls).setUpClass()
+ def resource_setup(cls):
+ super(QosSpecsV2TestJSON, cls).resource_setup()
# Create admin qos client
# Create a test shared qos-specs for tests
cls.qos_name = utils.rand_name(cls.__name__ + '-QoS')
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index 7040891..777d3de 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -20,9 +20,8 @@
class SnapshotV2MetadataTestJSON(base.BaseVolumeTest):
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(SnapshotV2MetadataTestJSON, cls).setUpClass()
+ def resource_setup(cls):
+ super(SnapshotV2MetadataTestJSON, cls).resource_setup()
cls.client = cls.snapshots_client
# Create a volume
cls.volume = cls.create_volume()
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index ac760aa..2ec8667 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -22,9 +22,8 @@
class VolumesV2MetadataTest(base.BaseVolumeTest):
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumesV2MetadataTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesV2MetadataTest, cls).resource_setup()
# Create a volume
cls.volume = cls.create_volume()
cls.volume_id = cls.volume['id']
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 4a6ba03..90ac9c1 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -26,8 +26,8 @@
class VolumesV2TransfersTest(base.BaseVolumeTest):
@classmethod
- def setUpClass(cls):
- super(VolumesV2TransfersTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesV2TransfersTest, cls).resource_setup()
# Add another tenant to test volume-transfer
if CONF.compute.allow_tenant_isolation:
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index c87878d..a9bc70a 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -24,9 +24,8 @@
class VolumesV2ActionsTest(base.BaseVolumeTest):
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumesV2ActionsTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesV2ActionsTest, cls).resource_setup()
cls.client = cls.volumes_client
cls.image_client = cls.os.image_client
@@ -45,12 +44,12 @@
self.image_client.wait_for_resource_deletion(image_id)
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
# Delete the test instance
cls.servers_client.delete_server(cls.server['id'])
cls.servers_client.wait_for_server_termination(cls.server['id'])
- super(VolumesV2ActionsTest, cls).tearDownClass()
+ super(VolumesV2ActionsTest, cls).resource_cleanup()
@test.stresstest(class_setup_per='process')
@test.attr(type='smoke')
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index c9e80aa..edd497c 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -23,9 +23,8 @@
class VolumesV2ExtendTest(base.BaseVolumeTest):
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumesV2ExtendTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesV2ExtendTest, cls).resource_setup()
cls.client = cls.volumes_client
@test.attr(type='gate')
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index a346a17..033beb4 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -26,8 +26,8 @@
class VolumesV2GetTest(base.BaseVolumeTest):
@classmethod
- def setUpClass(cls):
- super(VolumesV2GetTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesV2GetTest, cls).resource_setup()
cls.client = cls.volumes_client
cls.name_field = cls.special_fields['name_field']
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 272a41a..016e9ab 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -55,9 +55,8 @@
[str_vol(v) for v in fetched_list]))
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumesV2ListTestJSON, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesV2ListTestJSON, cls).resource_setup()
cls.client = cls.volumes_client
cls.name = cls.VOLUME_FIELDS[1]
@@ -72,12 +71,12 @@
cls.volume_id_list.append(volume['id'])
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
# Delete the created volumes
for volid in cls.volume_id_list:
cls.client.delete_volume(volid)
cls.client.wait_for_resource_deletion(volid)
- super(VolumesV2ListTestJSON, cls).tearDownClass()
+ super(VolumesV2ListTestJSON, cls).resource_cleanup()
def _list_by_param_value_and_assert(self, params, with_detail=False):
"""
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 5f0cffa..2b43c63 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -24,9 +24,8 @@
class VolumesV2NegativeTest(base.BaseVolumeTest):
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumesV2NegativeTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesV2NegativeTest, cls).resource_setup()
cls.client = cls.volumes_client
cls.name_field = cls.special_fields['name_field']
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 8390f03..78df1df 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -23,9 +23,8 @@
class VolumesV2SnapshotTestJSON(base.BaseVolumeTest):
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumesV2SnapshotTestJSON, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesV2SnapshotTestJSON, cls).resource_setup()
cls.volume_origin = cls.create_volume()
if not CONF.volume_feature_enabled.snapshot:
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index ddecda8..75a62a8 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -24,8 +24,8 @@
class VolumesV2SnapshotNegativeTestJSON(base.BaseVolumeTest):
@classmethod
- def setUpClass(cls):
- super(VolumesV2SnapshotNegativeTestJSON, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesV2SnapshotNegativeTestJSON, cls).resource_setup()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 3ae227d..cc56873 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -31,9 +31,8 @@
"""
@classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(VolumesV2ListTestJSON, cls).setUpClass()
+ def resource_setup(cls):
+ super(VolumesV2ListTestJSON, cls).resource_setup()
cls.client = cls.volumes_client
# Create 3 test volumes
@@ -47,12 +46,12 @@
cls.volume_id_list.append(volume['id'])
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
# Delete the created volumes
for volid in cls.volume_id_list:
cls.client.delete_volume(volid)
cls.client.wait_for_resource_deletion(volid)
- super(VolumesV2ListTestJSON, cls).tearDownClass()
+ super(VolumesV2ListTestJSON, cls).resource_cleanup()
@test.attr(type='gate')
def test_volume_list_details_with_multiple_params(self):
diff --git a/tempest/api_schema/request/compute/flavors.py b/tempest/api_schema/request/compute/flavors.py
index 8fe9e3a..adaaf27 100644
--- a/tempest/api_schema/request/compute/flavors.py
+++ b/tempest/api_schema/request/compute/flavors.py
@@ -40,14 +40,19 @@
"json-schema": {
"type": "object",
"properties": {
- "name": {"type": "string"},
- "ram": {"type": "integer", "minimum": 1},
- "vcpus": {"type": "integer", "minimum": 1},
- "disk": {"type": "integer"},
- "id": {"type": "integer"},
- "swap": {"type": "integer"},
- "rxtx_factor": {"type": "integer"},
- "OS-FLV-EXT-DATA:ephemeral": {"type": "integer"}
+ "flavor": {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string",
+ "exclude_tests": ["gen_str_min_length"]},
+ "ram": {"type": "integer", "minimum": 1},
+ "vcpus": {"type": "integer", "minimum": 1},
+ "disk": {"type": "integer"},
+ "id": {"type": "integer",
+ "exclude_tests": ["gen_none", "gen_string"]
+ },
+ }
+ }
}
}
}
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index c33589a..ca6d7fe 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -94,11 +94,11 @@
class ClientTestBase(tempest.test.BaseTestCase):
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
if not CONF.cli.enabled:
msg = "cli testing disabled"
raise cls.skipException(msg)
- super(ClientTestBase, cls).setUpClass()
+ super(ClientTestBase, cls).resource_setup()
def __init__(self, *args, **kwargs):
self.parser = tempest.cli.output_parser
diff --git a/tempest/cli/simple_read_only/compute/test_nova.py b/tempest/cli/simple_read_only/compute/test_nova.py
index 9bac7a6..6e5e077 100644
--- a/tempest/cli/simple_read_only/compute/test_nova.py
+++ b/tempest/cli/simple_read_only/compute/test_nova.py
@@ -41,11 +41,11 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
if not CONF.service_available.nova:
msg = ("%s skipped as Nova is not available" % cls.__name__)
raise cls.skipException(msg)
- super(SimpleReadOnlyNovaClientTest, cls).setUpClass()
+ super(SimpleReadOnlyNovaClientTest, cls).resource_setup()
def test_admin_fake_action(self):
self.assertRaises(exceptions.CommandFailed,
diff --git a/tempest/cli/simple_read_only/compute/test_nova_manage.py b/tempest/cli/simple_read_only/compute/test_nova_manage.py
index a4c7aa1..cff543f 100644
--- a/tempest/cli/simple_read_only/compute/test_nova_manage.py
+++ b/tempest/cli/simple_read_only/compute/test_nova_manage.py
@@ -36,7 +36,7 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
if not CONF.service_available.nova:
msg = ("%s skipped as Nova is not available" % cls.__name__)
raise cls.skipException(msg)
@@ -44,7 +44,7 @@
msg = ("%s skipped as *-manage commands not available"
% cls.__name__)
raise cls.skipException(msg)
- super(SimpleReadOnlyNovaManageTest, cls).setUpClass()
+ super(SimpleReadOnlyNovaManageTest, cls).resource_setup()
def test_admin_fake_action(self):
self.assertRaises(exceptions.CommandFailed,
diff --git a/tempest/cli/simple_read_only/data_processing/test_sahara.py b/tempest/cli/simple_read_only/data_processing/test_sahara.py
index 2c6e0e2..751a4ad 100644
--- a/tempest/cli/simple_read_only/data_processing/test_sahara.py
+++ b/tempest/cli/simple_read_only/data_processing/test_sahara.py
@@ -34,11 +34,11 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
if not CONF.service_available.sahara:
msg = "Skipping all Sahara cli tests because it is not available"
raise cls.skipException(msg)
- super(SimpleReadOnlySaharaClientTest, cls).setUpClass()
+ super(SimpleReadOnlySaharaClientTest, cls).resource_setup()
@test.attr(type='negative')
def test_sahara_fake_action(self):
diff --git a/tempest/cli/simple_read_only/image/test_glance.py b/tempest/cli/simple_read_only/image/test_glance.py
index 2fd8212..a9cbadb 100644
--- a/tempest/cli/simple_read_only/image/test_glance.py
+++ b/tempest/cli/simple_read_only/image/test_glance.py
@@ -34,11 +34,11 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
if not CONF.service_available.glance:
msg = ("%s skipped as Glance is not available" % cls.__name__)
raise cls.skipException(msg)
- super(SimpleReadOnlyGlanceClientTest, cls).setUpClass()
+ super(SimpleReadOnlyGlanceClientTest, cls).resource_setup()
def test_glance_fake_action(self):
self.assertRaises(exceptions.CommandFailed,
diff --git a/tempest/cli/simple_read_only/network/test_neutron.py b/tempest/cli/simple_read_only/network/test_neutron.py
index 87f6b67..f9f8906 100644
--- a/tempest/cli/simple_read_only/network/test_neutron.py
+++ b/tempest/cli/simple_read_only/network/test_neutron.py
@@ -35,11 +35,11 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
if (not CONF.service_available.neutron):
msg = "Skipping all Neutron cli tests because it is not available"
raise cls.skipException(msg)
- super(SimpleReadOnlyNeutronClientTest, cls).setUpClass()
+ super(SimpleReadOnlyNeutronClientTest, cls).resource_setup()
@test.attr(type='smoke')
def test_neutron_fake_action(self):
diff --git a/tempest/cli/simple_read_only/object_storage/test_swift.py b/tempest/cli/simple_read_only/object_storage/test_swift.py
index 069a384..a162660 100644
--- a/tempest/cli/simple_read_only/object_storage/test_swift.py
+++ b/tempest/cli/simple_read_only/object_storage/test_swift.py
@@ -31,11 +31,11 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
if not CONF.service_available.swift:
msg = ("%s skipped as Swift is not available" % cls.__name__)
raise cls.skipException(msg)
- super(SimpleReadOnlySwiftClientTest, cls).setUpClass()
+ super(SimpleReadOnlySwiftClientTest, cls).resource_setup()
def test_swift_fake_action(self):
self.assertRaises(exceptions.CommandFailed,
diff --git a/tempest/cli/simple_read_only/orchestration/test_heat.py b/tempest/cli/simple_read_only/orchestration/test_heat.py
index 430cdf1..7d7f8c9 100644
--- a/tempest/cli/simple_read_only/orchestration/test_heat.py
+++ b/tempest/cli/simple_read_only/orchestration/test_heat.py
@@ -32,12 +32,12 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(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()
+ super(SimpleReadOnlyHeatClientTest, cls).resource_setup()
cls.heat_template_path = os.path.join(os.path.dirname(
os.path.dirname(os.path.realpath(__file__))),
'heat_templates/heat_minimal.yaml')
diff --git a/tempest/cli/simple_read_only/telemetry/test_ceilometer.py b/tempest/cli/simple_read_only/telemetry/test_ceilometer.py
index 1d2822d..45b793b 100644
--- a/tempest/cli/simple_read_only/telemetry/test_ceilometer.py
+++ b/tempest/cli/simple_read_only/telemetry/test_ceilometer.py
@@ -32,12 +32,12 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
if (not CONF.service_available.ceilometer):
msg = ("Skipping all Ceilometer cli tests because it is "
"not available")
raise cls.skipException(msg)
- super(SimpleReadOnlyCeilometerClientTest, cls).setUpClass()
+ super(SimpleReadOnlyCeilometerClientTest, cls).resource_setup()
def test_ceilometer_meter_list(self):
self.ceilometer('meter-list')
diff --git a/tempest/cli/simple_read_only/volume/test_cinder.py b/tempest/cli/simple_read_only/volume/test_cinder.py
index e44a577..45f6c41 100644
--- a/tempest/cli/simple_read_only/volume/test_cinder.py
+++ b/tempest/cli/simple_read_only/volume/test_cinder.py
@@ -35,11 +35,11 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
if not CONF.service_available.cinder:
msg = ("%s skipped as Cinder is not available" % cls.__name__)
raise cls.skipException(msg)
- super(SimpleReadOnlyCinderClientTest, cls).setUpClass()
+ super(SimpleReadOnlyCinderClientTest, cls).resource_setup()
def test_cinder_fake_action(self):
self.assertRaises(exceptions.CommandFailed,
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
new file mode 100644
index 0000000..9ae3dfb
--- /dev/null
+++ b/tempest/cmd/cleanup.py
@@ -0,0 +1,301 @@
+#!/usr/bin/env python
+#
+# Copyright 2014 Dell Inc.
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: David Paterson
+
+"""
+Utility for cleaning up environment after Tempest run
+
+Runtime Arguments
+-----------------
+
+--init-saved-state: Before you can execute cleanup you must initialize
+the saved state by running it with the --init-saved-state flag
+(creating ./saved_state.json), which protects your deployment from
+cleanup deleting objects you want to keep. Typically you would run
+cleanup with --init-saved-state prior to a tempest run. If this is not
+the case saved_state.json must be edited, removing objects you want
+cleanup to delete.
+
+--dry-run: Creates a report (dry_run.json) of the tenants that will be
+cleaned up (in the "_tenants_to_clean" array), and the global objects
+that will be removed (tenants, users, flavors and images). Once
+cleanup is executed in normal mode, running it again with --dry-run
+should yield an empty report.
+
+**NOTE**: The _tenants_to_clean array in dry-run.json lists the
+tenants that cleanup will loop through and delete child objects, not
+delete the tenant itself. This may differ from the tenants array as you
+can clean the tempest and alternate tempest tenants but not delete the
+tenants themselves. This is actually the default behavior.
+
+**Normal mode**: running with no arguments, will query your deployment and
+build a list of objects to delete after filtering out out the objects
+found in saved_state.json and based on the
+--preserve-tempest-conf-objects and
+--delete-tempest-conf-objects flags.
+
+By default the tempest and alternate tempest users and tenants are not
+deleted and the admin user specified in tempest.conf is never deleted.
+
+Please run with --help to see full list of options.
+"""
+import argparse
+import json
+import sys
+
+from tempest import auth
+from tempest import clients
+from tempest.cmd import cleanup_service
+from tempest import config
+from tempest.openstack.common import log as logging
+
+SAVED_STATE_JSON = "saved_state.json"
+DRY_RUN_JSON = "dry_run.json"
+LOG = logging.getLogger(__name__)
+CONF = config.CONF
+
+
+class Cleanup(object):
+
+ def __init__(self):
+ self.admin_mgr = clients.AdminManager()
+ self.dry_run_data = {}
+ self.json_data = {}
+ self._init_options()
+
+ self.admin_id = ""
+ self.admin_role_id = ""
+ self.admin_tenant_id = ""
+ self._init_admin_ids()
+
+ self.admin_role_added = []
+
+ # available services
+ self.tenant_services = cleanup_service.get_tenant_cleanup_services()
+ self.global_services = cleanup_service.get_global_cleanup_services()
+ cleanup_service.init_conf()
+
+ def run(self):
+ opts = self.options
+ if opts.init_saved_state:
+ self._init_state()
+ return
+
+ self._load_json()
+ self._cleanup()
+
+ def _cleanup(self):
+ LOG.debug("Begin cleanup")
+ is_dry_run = self.options.dry_run
+ is_preserve = self.options.preserve_tempest_conf_objects
+ is_save_state = False
+
+ if is_dry_run:
+ self.dry_run_data["_tenants_to_clean"] = {}
+ f = open(DRY_RUN_JSON, 'w+')
+
+ admin_mgr = self.admin_mgr
+ # Always cleanup tempest and alt tempest tenants unless
+ # they are in saved state json. Therefore is_preserve is False
+ kwargs = {'data': self.dry_run_data,
+ 'is_dry_run': is_dry_run,
+ 'saved_state_json': self.json_data,
+ 'is_preserve': False,
+ 'is_save_state': is_save_state}
+ tenant_service = cleanup_service.TenantService(admin_mgr, **kwargs)
+ tenants = tenant_service.list()
+ LOG.debug("Process %s tenants" % len(tenants))
+
+ # Loop through list of tenants and clean them up.
+ for tenant in tenants:
+ self._add_admin(tenant['id'])
+ self._clean_tenant(tenant)
+
+ kwargs = {'data': self.dry_run_data,
+ 'is_dry_run': is_dry_run,
+ 'saved_state_json': self.json_data,
+ 'is_preserve': is_preserve,
+ 'is_save_state': is_save_state}
+ for service in self.global_services:
+ svc = service(admin_mgr, **kwargs)
+ svc.run()
+
+ if is_dry_run:
+ f.write(json.dumps(self.dry_run_data, sort_keys=True,
+ indent=2, separators=(',', ': ')))
+ f.close()
+
+ self._remove_admin_user_roles()
+
+ def _remove_admin_user_roles(self):
+ tenant_ids = self.admin_role_added
+ LOG.debug("Removing admin user roles where needed for tenants: %s"
+ % tenant_ids)
+ for tenant_id in tenant_ids:
+ self._remove_admin_role(tenant_id)
+
+ def _clean_tenant(self, tenant):
+ LOG.debug("Cleaning tenant: %s " % tenant['name'])
+ is_dry_run = self.options.dry_run
+ dry_run_data = self.dry_run_data
+ is_preserve = self.options.preserve_tempest_conf_objects
+ tenant_id = tenant['id']
+ tenant_name = tenant['name']
+ tenant_data = None
+ if is_dry_run:
+ tenant_data = dry_run_data["_tenants_to_clean"][tenant_id] = {}
+ tenant_data['name'] = tenant_name
+
+ kwargs = {"username": CONF.identity.admin_username,
+ "password": CONF.identity.admin_password,
+ "tenant_name": tenant['name']}
+ mgr = clients.Manager(credentials=auth.get_credentials(**kwargs))
+ kwargs = {'data': tenant_data,
+ 'is_dry_run': is_dry_run,
+ 'saved_state_json': None,
+ 'is_preserve': is_preserve,
+ 'is_save_state': False,
+ 'tenant_id': tenant_id}
+ for service in self.tenant_services:
+ svc = service(mgr, **kwargs)
+ svc.run()
+
+ def _init_admin_ids(self):
+ id_cl = self.admin_mgr.identity_client
+
+ tenant = id_cl.get_tenant_by_name(CONF.identity.admin_tenant_name)
+ self.admin_tenant_id = tenant['id']
+
+ user = id_cl.get_user_by_username(self.admin_tenant_id,
+ CONF.identity.admin_username)
+ self.admin_id = user['id']
+
+ _, roles = id_cl.list_roles()
+ for role in roles:
+ if role['name'] == CONF.identity.admin_role:
+ self.admin_role_id = role['id']
+ break
+
+ def _init_options(self):
+ parser = argparse.ArgumentParser(
+ description='Cleanup after tempest run')
+ parser.add_argument('--init-saved-state', action="store_true",
+ dest='init_saved_state', default=False,
+ help="Creates JSON file: " + SAVED_STATE_JSON +
+ ", representing the current state of your "
+ "deployment, specifically objects types "
+ "Tempest creates and destroys during a run. "
+ "You must run with this flag prior to "
+ "executing cleanup.")
+ parser.add_argument('--preserve-tempest-conf-objects',
+ action="store_true",
+ dest='preserve_tempest_conf_objects',
+ default=True, help="Do not delete the "
+ "tempest and alternate tempest users and "
+ "tenants, so they may be used for future "
+ "tempest runs. By default this is argument "
+ "is true.")
+ parser.add_argument('--delete-tempest-conf-objects',
+ action="store_false",
+ dest='preserve_tempest_conf_objects',
+ default=False,
+ help="Delete the tempest and "
+ "alternate tempest users and tenants.")
+ parser.add_argument('--dry-run', action="store_true",
+ dest='dry_run', default=False,
+ help="Generate JSON file:" + DRY_RUN_JSON +
+ ", that reports the objects that would have "
+ "been deleted had a full cleanup been run.")
+
+ self.options = parser.parse_args()
+
+ def _add_admin(self, tenant_id):
+ id_cl = self.admin_mgr.identity_client
+ needs_role = True
+ _, roles = id_cl.list_user_roles(tenant_id, self.admin_id)
+ for role in roles:
+ if role['id'] == self.admin_role_id:
+ needs_role = False
+ LOG.debug("User already had admin privilege for this tenant")
+ if needs_role:
+ LOG.debug("Adding admin priviledge for : %s" % tenant_id)
+ id_cl.assign_user_role(tenant_id, self.admin_id,
+ self.admin_role_id)
+ self.admin_role_added.append(tenant_id)
+
+ def _remove_admin_role(self, tenant_id):
+ LOG.debug("Remove admin user role for tenant: %s" % tenant_id)
+ # Must initialize AdminManager for each user role
+ # Otherwise authentication exception is thrown, weird
+ id_cl = clients.AdminManager().identity_client
+ if (self._tenant_exists(tenant_id)):
+ try:
+ id_cl.remove_user_role(tenant_id, self.admin_id,
+ self.admin_role_id)
+ except Exception as ex:
+ LOG.exception("Failed removing role from tenant which still"
+ "exists, exception: %s" % ex)
+
+ def _tenant_exists(self, tenant_id):
+ id_cl = self.admin_mgr.identity_client
+ try:
+ t = id_cl.get_tenant(tenant_id)
+ LOG.debug("Tenant is: %s" % str(t))
+ return True
+ except Exception as ex:
+ LOG.debug("Tenant no longer exists? %s" % ex)
+ return False
+
+ def _init_state(self):
+ LOG.debug("Initializing saved state.")
+ data = {}
+ admin_mgr = self.admin_mgr
+ kwargs = {'data': data,
+ 'is_dry_run': False,
+ 'saved_state_json': data,
+ 'is_preserve': False,
+ 'is_save_state': True}
+ for service in self.global_services:
+ svc = service(admin_mgr, **kwargs)
+ svc.run()
+
+ f = open(SAVED_STATE_JSON, 'w+')
+ f.write(json.dumps(data,
+ sort_keys=True, indent=2, separators=(',', ': ')))
+ f.close()
+
+ def _load_json(self):
+ try:
+ json_file = open(SAVED_STATE_JSON)
+ self.json_data = json.load(json_file)
+ json_file.close()
+ except IOError as ex:
+ LOG.exception("Failed loading saved state, please be sure you"
+ " have first run cleanup with --init-saved-state "
+ "flag prior to running tempest. Exception: %s" % ex)
+ sys.exit(ex)
+ except Exception as ex:
+ LOG.exception("Exception parsing saved state json : %s" % ex)
+ sys.exit(ex)
+
+
+def main():
+ cleanup = Cleanup()
+ cleanup.run()
+ LOG.info('Cleanup finished!')
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
new file mode 100644
index 0000000..f5f0db3
--- /dev/null
+++ b/tempest/cmd/cleanup_service.py
@@ -0,0 +1,1066 @@
+#!/usr/bin/env python
+
+# Copyright 2014 Dell Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+'''
+Created on Sep 3, 2014
+
+@author: David_Paterson
+'''
+from tempest import config
+from tempest.openstack.common import log as logging
+from tempest import test
+
+LOG = logging.getLogger(__name__)
+CONF = config.CONF
+
+CONF_USERS = None
+CONF_TENANTS = None
+CONF_PUB_NETWORK = None
+CONF_PRIV_NETWORK_NAME = None
+CONF_PUB_ROUTER = None
+CONF_FLAVORS = None
+CONF_IMAGES = None
+
+IS_CEILOMETER = None
+IS_CINDER = None
+IS_GLANCE = None
+IS_HEAT = None
+IS_NEUTRON = None
+IS_NOVA = None
+
+
+def init_conf():
+ global CONF_USERS
+ global CONF_TENANTS
+ global CONF_PUB_NETWORK
+ global CONF_PRIV_NETWORK_NAME
+ global CONF_PUB_ROUTER
+ global CONF_FLAVORS
+ global CONF_IMAGES
+
+ global IS_CEILOMETER
+ global IS_CINDER
+ global IS_GLANCE
+ global IS_HEAT
+ global IS_NEUTRON
+ global IS_NOVA
+
+ CONF_USERS = [CONF.identity.admin_username, CONF.identity.username,
+ CONF.identity.alt_username]
+ CONF_TENANTS = [CONF.identity.admin_tenant_name,
+ CONF.identity.tenant_name,
+ CONF.identity.alt_tenant_name]
+ CONF_PUB_NETWORK = CONF.network.public_network_id
+ CONF_PRIV_NETWORK_NAME = CONF.compute.fixed_network_name
+ CONF_PUB_ROUTER = CONF.network.public_router_id
+ CONF_FLAVORS = [CONF.compute.flavor_ref, CONF.compute.flavor_ref_alt]
+ CONF_IMAGES = [CONF.compute.image_ref, CONF.compute.image_ref_alt]
+
+ IS_CEILOMETER = CONF.service_available.ceilometer
+ IS_CINDER = CONF.service_available.cinder
+ IS_GLANCE = CONF.service_available.glance
+ IS_HEAT = CONF.service_available.heat
+ IS_NEUTRON = CONF.service_available.neutron
+ IS_NOVA = CONF.service_available.nova
+
+
+class BaseService(object):
+ def __init__(self, kwargs):
+ self.client = None
+ for key, value in kwargs.items():
+ setattr(self, key, value)
+
+ def _filter_by_tenant_id(self, item_list):
+ if (item_list is None
+ or len(item_list) == 0
+ or not hasattr(self, 'tenant_id')
+ or self.tenant_id is None
+ or 'tenant_id' not in item_list[0]):
+ return item_list
+
+ _filtered_list = []
+ for item in item_list:
+ if item['tenant_id'] == self.tenant_id:
+ _filtered_list.append(item)
+ return _filtered_list
+
+ def list(self):
+ pass
+
+ def delete(self):
+ pass
+
+ def dry_run(self):
+ pass
+
+ def save_state(self):
+ pass
+
+ def run(self):
+ if self.is_dry_run:
+ self.dry_run()
+ elif self.is_save_state:
+ self.save_state()
+ else:
+ self.delete()
+
+
+class SnapshotService(BaseService):
+
+ def __init__(self, manager, **kwargs):
+ super(SnapshotService, self).__init__(kwargs)
+ self.client = manager.snapshots_client
+
+ def list(self):
+ client = self.client
+ __, snaps = client.list_snapshots()
+ LOG.debug("List count, %s Snapshots" % len(snaps))
+ return snaps
+
+ def delete(self):
+ snaps = self.list()
+ client = self.client
+ for snap in snaps:
+ try:
+ client.delete_snapshot(snap['id'])
+ except Exception as e:
+ LOG.exception("Delete Snapshot exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ snaps = self.list()
+ self.data['snapshots'] = snaps
+
+
+class ServerService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(ServerService, self).__init__(kwargs)
+ self.client = manager.servers_client
+
+ def list(self):
+ client = self.client
+ _, servers_body = client.list_servers()
+ servers = servers_body['servers']
+ LOG.debug("List count, %s Servers" % len(servers))
+ return servers
+
+ def delete(self):
+ client = self.client
+ servers = self.list()
+ for server in servers:
+ try:
+ client.delete_server(server['id'])
+ except Exception as e:
+ LOG.exception("Delete Server exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ servers = self.list()
+ self.data['servers'] = servers
+
+
+class ServerGroupService(ServerService):
+
+ def list(self):
+ client = self.client
+ _, sgs = client.list_server_groups()
+ LOG.debug("List count, %s Server Groups" % len(sgs))
+ return sgs
+
+ def delete(self):
+ client = self.client
+ sgs = self.list()
+ for sg in sgs:
+ try:
+ client.delete_server_group(sg['id'])
+ except Exception as e:
+ LOG.exception("Delete Server Group exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ sgs = self.list()
+ self.data['server_groups'] = sgs
+
+
+class StackService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(StackService, self).__init__(kwargs)
+ self.client = manager.orchestration_client
+
+ def list(self):
+ client = self.client
+ _, stacks = client.list_stacks()
+ LOG.debug("List count, %s Stacks" % len(stacks))
+ return stacks
+
+ def delete(self):
+ client = self.client
+ stacks = self.list()
+ for stack in stacks:
+ try:
+ client.delete_stack(stack['id'])
+ except Exception as e:
+ LOG.exception("Delete Stack exception: %s " % e)
+ pass
+
+ def dry_run(self):
+ stacks = self.list()
+ self.data['stacks'] = stacks
+
+
+class KeyPairService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(KeyPairService, self).__init__(kwargs)
+ self.client = manager.keypairs_client
+
+ def list(self):
+ client = self.client
+ _, keypairs = client.list_keypairs()
+ LOG.debug("List count, %s Keypairs" % len(keypairs))
+ return keypairs
+
+ def delete(self):
+ client = self.client
+ keypairs = self.list()
+ for k in keypairs:
+ try:
+ name = k['keypair']['name']
+ client.delete_keypair(name)
+ except Exception as e:
+ LOG.exception("Delete Keypairs exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ keypairs = self.list()
+ self.data['keypairs'] = keypairs
+
+
+class SecurityGroupService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(SecurityGroupService, self).__init__(kwargs)
+ self.client = manager.security_groups_client
+
+ def list(self):
+ client = self.client
+ _, secgrps = client.list_security_groups()
+ secgrp_del = [grp for grp in secgrps if grp['name'] != 'default']
+ LOG.debug("List count, %s Security Groups" % len(secgrp_del))
+ return secgrp_del
+
+ def delete(self):
+ client = self.client
+ secgrp_del = self.list()
+ for g in secgrp_del:
+ try:
+ client.delete_security_group(g['id'])
+ except Exception as e:
+ LOG.exception("Delete Security Groups exception: %s" % e)
+
+ def dry_run(self):
+ secgrp_del = self.list()
+ self.data['security_groups'] = secgrp_del
+
+
+class FloatingIpService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(FloatingIpService, self).__init__(kwargs)
+ self.client = manager.floating_ips_client
+
+ def list(self):
+ client = self.client
+ _, floating_ips = client.list_floating_ips()
+ LOG.debug("List count, %s Floating IPs" % len(floating_ips))
+ return floating_ips
+
+ def delete(self):
+ client = self.client
+ floating_ips = self.list()
+ for f in floating_ips:
+ try:
+ client.delete_floating_ip(f['id'])
+ except Exception as e:
+ LOG.exception("Delete Floating IPs exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ floating_ips = self.list()
+ self.data['floating_ips'] = floating_ips
+
+
+class VolumeService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(VolumeService, self).__init__(kwargs)
+ self.client = manager.volumes_client
+
+ def list(self):
+ client = self.client
+ _, vols = client.list_volumes()
+ LOG.debug("List count, %s Volumes" % len(vols))
+ return vols
+
+ def delete(self):
+ client = self.client
+ vols = self.list()
+ for v in vols:
+ try:
+ client.delete_volume(v['id'])
+ except Exception as e:
+ LOG.exception("Delete Volume exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ vols = self.list()
+ self.data['volumes'] = vols
+
+
+# Begin network service classes
+class NetworkService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(NetworkService, self).__init__(kwargs)
+ self.client = manager.network_client
+
+ def list(self):
+ client = self.client
+ _, networks = client.list_networks()
+ networks = self._filter_by_tenant_id(networks['networks'])
+ # filter out networks declared in tempest.conf
+ if self.is_preserve:
+ networks = [network for network in networks
+ if (network['name'] != CONF_PRIV_NETWORK_NAME
+ and network['id'] != CONF_PUB_NETWORK)]
+ LOG.debug("List count, %s Networks" % networks)
+ return networks
+
+ def delete(self):
+ client = self.client
+ networks = self.list()
+ for n in networks:
+ try:
+ client.delete_network(n['id'])
+ except Exception as e:
+ LOG.exception("Delete Network exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ networks = self.list()
+ self.data['networks'] = networks
+
+
+class NetworkIpSecPolicyService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, ipsecpols = client.list_ipsecpolicies()
+ ipsecpols = ipsecpols['ipsecpolicies']
+ ipsecpols = self._filter_by_tenant_id(ipsecpols)
+ LOG.debug("List count, %s IP Security Policies" % len(ipsecpols))
+ return ipsecpols
+
+ def delete(self):
+ client = self.client
+ ipsecpols = self.list()
+ for ipsecpol in ipsecpols:
+ try:
+ client.delete_ipsecpolicy(ipsecpol['id'])
+ except Exception as e:
+ LOG.exception("Delete IP Securty Policy exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ ipsecpols = self.list()
+ self.data['ip_security_policies'] = ipsecpols
+
+
+class NetworkFwPolicyService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, fwpols = client.list_firewall_policies()
+ fwpols = fwpols['firewall_policies']
+ fwpols = self._filter_by_tenant_id(fwpols)
+ LOG.debug("List count, %s Firewall Policies" % len(fwpols))
+ return fwpols
+
+ def delete(self):
+ client = self.client
+ fwpols = self.list()
+ for fwpol in fwpols:
+ try:
+ client.delete_firewall_policy(fwpol['id'])
+ except Exception as e:
+ LOG.exception("Delete Firewall Policy exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ fwpols = self.list()
+ self.data['firewall_policies'] = fwpols
+
+
+class NetworkFwRulesService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, fwrules = client.list_firewall_rules()
+ fwrules = fwrules['firewall_rules']
+ fwrules = self._filter_by_tenant_id(fwrules)
+ LOG.debug("List count, %s Firewall Rules" % len(fwrules))
+ return fwrules
+
+ def delete(self):
+ client = self.client
+ fwrules = self.list()
+ for fwrule in fwrules:
+ try:
+ client.delete_firewall_rule(fwrule['id'])
+ except Exception as e:
+ LOG.exception("Delete Firewall Rule exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ fwrules = self.list()
+ self.data['firewall_rules'] = fwrules
+
+
+class NetworkIkePolicyService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, ikepols = client.list_ikepolicies()
+ ikepols = ikepols['ikepolicies']
+ ikepols = self._filter_by_tenant_id(ikepols)
+ LOG.debug("List count, %s IKE Policies" % len(ikepols))
+ return ikepols
+
+ def delete(self):
+ client = self.client
+ ikepols = self.list()
+ for ikepol in ikepols:
+ try:
+ client.delete_firewall_rule(ikepol['id'])
+ except Exception as e:
+ LOG.exception("Delete IKE Policy exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ ikepols = self.list()
+ self.data['ike_policies'] = ikepols
+
+
+class NetworkVpnServiceService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, vpnsrvs = client.list_vpnservices()
+ vpnsrvs = vpnsrvs['vpnservices']
+ vpnsrvs = self._filter_by_tenant_id(vpnsrvs)
+ LOG.debug("List count, %s VPN Services" % len(vpnsrvs))
+ return vpnsrvs
+
+ def delete(self):
+ client = self.client
+ vpnsrvs = self.list()
+ for vpnsrv in vpnsrvs:
+ try:
+ client.delete_vpnservice(vpnsrv['id'])
+ except Exception as e:
+ LOG.exception("Delete VPN Service exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ vpnsrvs = self.list()
+ self.data['vpn_services'] = vpnsrvs
+
+
+class NetworkFloatingIpService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, flips = client.list_floatingips()
+ flips = flips['floatingips']
+ flips = self._filter_by_tenant_id(flips)
+ LOG.debug("List count, %s Network Floating IPs" % len(flips))
+ return flips
+
+ def delete(self):
+ client = self.client
+ flips = self.list()
+ for flip in flips:
+ try:
+ client.delete_floatingip(flip['id'])
+ except Exception as e:
+ LOG.exception("Delete Network Floating IP exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ flips = self.list()
+ self.data['floating_ips'] = flips
+
+
+class NetworkRouterService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, routers = client.list_routers()
+ routers = routers['routers']
+ routers = self._filter_by_tenant_id(routers)
+ if self.is_preserve:
+ routers = [router for router in routers
+ if router['id'] != CONF_PUB_ROUTER]
+
+ LOG.debug("List count, %s Routers" % len(routers))
+ return routers
+
+ def delete(self):
+ client = self.client
+ routers = self.list()
+ for router in routers:
+ try:
+ rid = router['id']
+ _, ports = client.list_router_interfaces(rid)
+ ports = ports['ports']
+ for port in ports:
+ subid = port['fixed_ips'][0]['subnet_id']
+ client.remove_router_interface_with_subnet_id(rid, subid)
+ client.delete_router(rid)
+ except Exception as e:
+ LOG.exception("Delete Router exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ routers = self.list()
+ self.data['routers'] = routers
+
+
+class NetworkHealthMonitorService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, hms = client.list_health_monitors()
+ hms = hms['health_monitors']
+ hms = self._filter_by_tenant_id(hms)
+ LOG.debug("List count, %s Health Monitors" % len(hms))
+ return hms
+
+ def delete(self):
+ client = self.client
+ hms = self.list()
+ for hm in hms:
+ try:
+ client.delete_health_monitor(hm['id'])
+ except Exception as e:
+ LOG.exception("Delete Health Monitor exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ hms = self.list()
+ self.data['health_monitors'] = hms
+
+
+class NetworkMemberService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, members = client.list_members()
+ members = members['members']
+ members = self._filter_by_tenant_id(members)
+ LOG.debug("List count, %s Members" % len(members))
+ return members
+
+ def delete(self):
+ client = self.client
+ members = self.list()
+ for member in members:
+ try:
+ client.delete_member(member['id'])
+ except Exception as e:
+ LOG.exception("Delete Member exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ members = self.list()
+ self.data['members'] = members
+
+
+class NetworkVipService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, vips = client.list_vips()
+ vips = vips['vips']
+ vips = self._filter_by_tenant_id(vips)
+ LOG.debug("List count, %s VIPs" % len(vips))
+ return vips
+
+ def delete(self):
+ client = self.client
+ vips = self.list()
+ for vip in vips:
+ try:
+ client.delete_vip(vip['id'])
+ except Exception as e:
+ LOG.exception("Delete VIP exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ vips = self.list()
+ self.data['vips'] = vips
+
+
+class NetworkPoolService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, pools = client.list_pools()
+ pools = pools['pools']
+ pools = self._filter_by_tenant_id(pools)
+ LOG.debug("List count, %s Pools" % len(pools))
+ return pools
+
+ def delete(self):
+ client = self.client
+ pools = self.list()
+ for pool in pools:
+ try:
+ client.delete_pool(pool['id'])
+ except Exception as e:
+ LOG.exception("Delete Pool exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ pools = self.list()
+ self.data['pools'] = pools
+
+
+class NetworMeteringLabelRuleService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, rules = client.list_metering_label_rules()
+ rules = rules['metering_label_rules']
+ rules = self._filter_by_tenant_id(rules)
+ LOG.debug("List count, %s Metering Label Rules" % len(rules))
+ return rules
+
+ def delete(self):
+ client = self.client
+ rules = self.list()
+ for rule in rules:
+ try:
+ client.delete_metering_label_rule(rule['id'])
+ except Exception as e:
+ LOG.exception("Delete Metering Label Rule exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ rules = self.list()
+ self.data['rules'] = rules
+
+
+class NetworMeteringLabelService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, labels = client.list_metering_labels()
+ labels = labels['metering_labels']
+ labels = self._filter_by_tenant_id(labels)
+ LOG.debug("List count, %s Metering Labels" % len(labels))
+ return labels
+
+ def delete(self):
+ client = self.client
+ labels = self.list()
+ for label in labels:
+ try:
+ client.delete_metering_label(label['id'])
+ except Exception as e:
+ LOG.exception("Delete Metering Label exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ labels = self.list()
+ self.data['labels'] = labels
+
+
+class NetworkPortService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, ports = client.list_ports()
+ ports = ports['ports']
+ ports = self._filter_by_tenant_id(ports)
+ LOG.debug("List count, %s Ports" % len(ports))
+ return ports
+
+ def delete(self):
+ client = self.client
+ ports = self.list()
+ for port in ports:
+ try:
+ client.delete_port(port['id'])
+ except Exception as e:
+ LOG.exception("Delete Port exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ ports = self.list()
+ self.data['ports'] = ports
+
+
+class NetworkSubnetService(NetworkService):
+
+ def list(self):
+ client = self.client
+ _, subnets = client.list_subnets()
+ subnets = subnets['subnets']
+ subnets = self._filter_by_tenant_id(subnets)
+ LOG.debug("List count, %s Subnets" % len(subnets))
+ return subnets
+
+ def delete(self):
+ client = self.client
+ subnets = self.list()
+ for subnet in subnets:
+ try:
+ client.delete_subnet(subnet['id'])
+ except Exception as e:
+ LOG.exception("Delete Subnet exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ subnets = self.list()
+ self.data['subnets'] = subnets
+
+
+# Telemetry services
+class TelemetryAlarmService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(TelemetryAlarmService, self).__init__(kwargs)
+ self.client = manager.telemetry_client
+
+ def list(self):
+ client = self.client
+ _, alarms = client.list_alarms()
+ LOG.debug("List count, %s Alarms" % len(alarms))
+ return alarms
+
+ def delete(self):
+ client = self.client
+ alarms = self.list()
+ for alarm in alarms:
+ try:
+ client.delete_alarm(alarm['id'])
+ except Exception as e:
+ LOG.exception("Delete Alarms exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ alarms = self.list()
+ self.data['alarms'] = alarms
+
+
+# begin global services
+class FlavorService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(FlavorService, self).__init__(kwargs)
+ self.client = manager.flavors_client
+
+ def list(self):
+ client = self.client
+ _, flavors = client.list_flavors({"is_public": None})
+ if not self.is_save_state:
+ # recreate list removing saved flavors
+ flavors = [flavor for flavor in flavors if flavor['id']
+ not in self.saved_state_json['flavors'].keys()]
+
+ if self.is_preserve:
+ flavors = [flavor for flavor in flavors
+ if flavor['id'] not in CONF_FLAVORS]
+ LOG.debug("List count, %s Flavors after reconcile" % len(flavors))
+ return flavors
+
+ def delete(self):
+ client = self.client
+ flavors = self.list()
+ for flavor in flavors:
+ try:
+ client.delete_flavor(flavor['id'])
+ except Exception as e:
+ LOG.exception("Delete Flavor exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ flavors = self.list()
+ self.data['flavors'] = flavors
+
+ def save_state(self):
+ flavors = self.list()
+ flavor_data = self.data['flavors'] = {}
+ for flavor in flavors:
+ flavor_data[flavor['id']] = flavor['name']
+
+
+class ImageService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(ImageService, self).__init__(kwargs)
+ self.client = manager.images_client
+
+ def list(self):
+ client = self.client
+ _, images = client.list_images({"all_tenants": True})
+ if not self.is_save_state:
+ images = [image for image in images if image['id']
+ not in self.saved_state_json['images'].keys()]
+ if self.is_preserve:
+ images = [image for image in images
+ if image['id'] not in CONF_IMAGES]
+ LOG.debug("List count, %s Images after reconcile" % len(images))
+ return images
+
+ def delete(self):
+ client = self.client
+ images = self.list()
+ for image in images:
+ try:
+ client.delete_image(image['id'])
+ except Exception as e:
+ LOG.exception("Delete Image exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ images = self.list()
+ self.data['images'] = images
+
+ def save_state(self):
+ images = self.list()
+ image_data = self.data['images'] = {}
+ for image in images:
+ image_data[image['id']] = image['name']
+
+
+class IdentityService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(IdentityService, self).__init__(kwargs)
+ self.client = manager.identity_client
+
+
+class UserService(IdentityService):
+
+ def list(self):
+ client = self.client
+ _, users = client.get_users()
+
+ if not self.is_save_state:
+ users = [user for user in users if user['id']
+ not in self.saved_state_json['users'].keys()]
+
+ if self.is_preserve:
+ users = [user for user in users if user['name']
+ not in CONF_USERS]
+
+ elif not self.is_save_state: # Never delete admin user
+ users = [user for user in users if user['name'] !=
+ CONF.identity.admin_username]
+
+ LOG.debug("List count, %s Users after reconcile" % len(users))
+ return users
+
+ def delete(self):
+ client = self.client
+ users = self.list()
+ for user in users:
+ try:
+ client.delete_user(user['id'])
+ except Exception as e:
+ LOG.exception("Delete User exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ users = self.list()
+ self.data['users'] = users
+
+ def save_state(self):
+ users = self.list()
+ user_data = self.data['users'] = {}
+ for user in users:
+ user_data[user['id']] = user['name']
+
+
+class RoleService(IdentityService):
+
+ def list(self):
+ client = self.client
+ try:
+ _, roles = client.list_roles()
+ # reconcile roles with saved state and never list admin role
+ if not self.is_save_state:
+ roles = [role for role in roles if
+ (role['id'] not in
+ self.saved_state_json['roles'].keys()
+ and role['name'] != CONF.identity.admin_role)]
+ LOG.debug("List count, %s Roles after reconcile" % len(roles))
+ return roles
+ except Exception as ex:
+ LOG.exception("Cannot retrieve Roles, exception: %s" % ex)
+ return []
+
+ def delete(self):
+ client = self.client
+ roles = self.list()
+ for role in roles:
+ try:
+ client.delete_role(role['id'])
+ except Exception as e:
+ LOG.exception("Delete Role exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ roles = self.list()
+ self.data['roles'] = roles
+
+ def save_state(self):
+ roles = self.list()
+ role_data = self.data['roles'] = {}
+ for role in roles:
+ role_data[role['id']] = role['name']
+
+
+class TenantService(IdentityService):
+
+ def list(self):
+ client = self.client
+ _, tenants = client.list_tenants()
+ if not self.is_save_state:
+ tenants = [tenant for tenant in tenants if (tenant['id']
+ not in self.saved_state_json['tenants'].keys()
+ and tenant['name'] != CONF.identity.admin_tenant_name)]
+
+ if self.is_preserve:
+ tenants = [tenant for tenant in tenants if tenant['name']
+ not in CONF_TENANTS]
+
+ LOG.debug("List count, %s Tenants after reconcile" % len(tenants))
+ return tenants
+
+ def delete(self):
+ client = self.client
+ tenants = self.list()
+ for tenant in tenants:
+ try:
+ client.delete_tenant(tenant['id'])
+ except Exception as e:
+ LOG.exception("Delete Tenant exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ tenants = self.list()
+ self.data['tenants'] = tenants
+
+ def save_state(self):
+ tenants = self.list()
+ tenant_data = self.data['tenants'] = {}
+ for tenant in tenants:
+ tenant_data[tenant['id']] = tenant['name']
+
+
+class DomainService(BaseService):
+
+ def __init__(self, manager, **kwargs):
+ super(DomainService, self).__init__(kwargs)
+ self.client = manager.identity_v3_client
+
+ def list(self):
+ client = self.client
+ _, domains = client.list_domains()
+ if not self.is_save_state:
+ domains = [domain for domain in domains if domain['id']
+ not in self.saved_state_json['domains'].keys()]
+
+ LOG.debug("List count, %s Domains after reconcile" % len(domains))
+ return domains
+
+ def delete(self):
+ client = self.client
+ domains = self.list()
+ for domain in domains:
+ try:
+ client.update_domain(domain['id'], enabled=False)
+ client.delete_domain(domain['id'])
+ except Exception as e:
+ LOG.exception("Delete Domain exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ domains = self.list()
+ self.data['domains'] = domains
+
+ def save_state(self):
+ domains = self.list()
+ domain_data = self.data['domains'] = {}
+ for domain in domains:
+ domain_data[domain['id']] = domain['name']
+
+
+def get_tenant_cleanup_services():
+ tenant_services = []
+
+ if IS_CEILOMETER:
+ tenant_services.append(TelemetryAlarmService)
+ if IS_NOVA:
+ tenant_services.append(ServerService)
+ tenant_services.append(KeyPairService)
+ tenant_services.append(SecurityGroupService)
+ tenant_services.append(ServerGroupService)
+ if not IS_NEUTRON:
+ tenant_services.append(FloatingIpService)
+ if IS_HEAT:
+ tenant_services.append(StackService)
+ if IS_NEUTRON:
+ if test.is_extension_enabled('vpnaas', 'network'):
+ tenant_services.append(NetworkIpSecPolicyService)
+ tenant_services.append(NetworkIkePolicyService)
+ tenant_services.append(NetworkVpnServiceService)
+ if test.is_extension_enabled('fwaas', 'network'):
+ tenant_services.append(NetworkFwPolicyService)
+ tenant_services.append(NetworkFwRulesService)
+ if test.is_extension_enabled('lbaas', 'network'):
+ tenant_services.append(NetworkHealthMonitorService)
+ tenant_services.append(NetworkMemberService)
+ tenant_services.append(NetworkVipService)
+ tenant_services.append(NetworkPoolService)
+ if test.is_extension_enabled('metering', 'network'):
+ tenant_services.append(NetworMeteringLabelRuleService)
+ tenant_services.append(NetworMeteringLabelService)
+ tenant_services.append(NetworkRouterService)
+ tenant_services.append(NetworkFloatingIpService)
+ tenant_services.append(NetworkPortService)
+ tenant_services.append(NetworkSubnetService)
+ tenant_services.append(NetworkService)
+ if IS_CINDER:
+ tenant_services.append(SnapshotService)
+ tenant_services.append(VolumeService)
+ return tenant_services
+
+
+def get_global_cleanup_services():
+ global_services = []
+ if IS_NOVA:
+ global_services.append(FlavorService)
+ if IS_GLANCE:
+ global_services.append(ImageService)
+ global_services.append(UserService)
+ global_services.append(TenantService)
+ global_services.append(DomainService)
+ global_services.append(RoleService)
+ return global_services
diff --git a/tempest/common/generator/base_generator.py b/tempest/common/generator/base_generator.py
index 0398af1..3f405b1 100644
--- a/tempest/common/generator/base_generator.py
+++ b/tempest/common/generator/base_generator.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
import functools
import jsonschema
@@ -30,9 +31,11 @@
return expected_result
-def generator_type(*args):
+def generator_type(*args, **kwargs):
def wrapper(func):
func.types = args
+ for key in kwargs:
+ setattr(func, key, kwargs[key])
return func
return wrapper
@@ -106,37 +109,74 @@
jsonschema.Draft4Validator.check_schema(schema['json-schema'])
jsonschema.validate(schema, self.schema)
- def generate(self, schema):
+ def generate_scenarios(self, schema, path=None):
"""
- Generate an json dictionary based on a schema.
- Only one value is mis-generated for each dictionary created.
+ Generates the scenario (all possible test cases) out of the given
+ schema.
- Any generator must return a list of tuples or a single tuple.
- The values of this tuple are:
- result[0]: Name of the test
- result[1]: json schema for the test
- result[2]: expected result of the test (can be None)
+ :param schema: a dict style schema (see ``BasicGeneratorSet.schema``)
+ :param path: the schema path if the given schema is a subschema
"""
- LOG.debug("generate_invalid: %s" % schema)
- schema_type = schema["type"]
- if isinstance(schema_type, list):
+ schema_type = schema['type']
+ scenarios = []
+
+ if schema_type == 'object':
+ properties = schema["properties"]
+ for attribute, definition in properties.iteritems():
+ current_path = copy.copy(path)
+ if path is not None:
+ current_path.append(attribute)
+ else:
+ current_path = [attribute]
+ scenarios.extend(
+ self.generate_scenarios(definition, current_path))
+ elif isinstance(schema_type, list):
if "integer" in schema_type:
schema_type = "integer"
else:
raise Exception("non-integer list types not supported")
- result = []
- if schema_type not in self.types_dict:
- raise TypeError("generator (%s) doesn't support type: %s"
- % (self.__class__.__name__, schema_type))
for generator in self.types_dict[schema_type]:
- ret = generator(schema)
- if ret is not None:
- if isinstance(ret, list):
- result.extend(ret)
- elif isinstance(ret, tuple):
- result.append(ret)
- else:
- raise Exception("generator (%s) returns invalid result: %s"
- % (generator, ret))
- LOG.debug("result: %s" % result)
- return result
+ if hasattr(generator, "needed_property"):
+ prop = generator.needed_property
+ if (prop not in schema or
+ schema[prop] is None or
+ schema[prop] is False):
+ continue
+
+ name = generator.__name__
+ if ("exclude_tests" in schema and
+ name in schema["exclude_tests"]):
+ continue
+ if path is not None:
+ name = "%s_%s" % ("_".join(path), name)
+ scenarios.append({
+ "_negtest_name": name,
+ "_negtest_generator": generator,
+ "_negtest_schema": schema,
+ "_negtest_path": path})
+ return scenarios
+
+ def generate_payload(self, test, schema):
+ """
+ Generates one jsonschema out of the given test. It's mandatory to use
+ generate_scenarios before to register all needed variables to the test.
+
+ :param test: A test object (scenario) with all _negtest variables on it
+ :param schema: schema for the test
+ """
+ generator = test._negtest_generator
+ ret = generator(test._negtest_schema)
+ path = copy.copy(test._negtest_path)
+ expected_result = None
+
+ if ret is not None:
+ generator_result = generator(test._negtest_schema)
+ invalid_snippet = generator_result[1]
+ expected_result = generator_result[2]
+ element = path.pop()
+ if len(path) > 0:
+ schema_snip = reduce(dict.get, path, schema)
+ schema_snip[element] = invalid_snippet
+ else:
+ schema[element] = invalid_snippet
+ return expected_result
diff --git a/tempest/common/generator/negative_generator.py b/tempest/common/generator/negative_generator.py
index 4f3d2cd..1d5ed43 100644
--- a/tempest/common/generator/negative_generator.py
+++ b/tempest/common/generator/negative_generator.py
@@ -47,65 +47,32 @@
if min_length > 0:
return "x" * (min_length - 1)
- @base.generator_type("string")
+ @base.generator_type("string", needed_property="maxLength")
@base.simple_generator
def gen_str_max_length(self, schema):
max_length = schema.get("maxLength", -1)
- if max_length > -1:
- return "x" * (max_length + 1)
+ return "x" * (max_length + 1)
- @base.generator_type("integer")
+ @base.generator_type("integer", needed_property="minimum")
@base.simple_generator
def gen_int_min(self, schema):
- if "minimum" in schema:
- minimum = schema["minimum"]
- if "exclusiveMinimum" not in schema:
- minimum -= 1
- return minimum
+ minimum = schema["minimum"]
+ if "exclusiveMinimum" not in schema:
+ minimum -= 1
+ return minimum
- @base.generator_type("integer")
+ @base.generator_type("integer", needed_property="maximum")
@base.simple_generator
def gen_int_max(self, schema):
- if "maximum" in schema:
- maximum = schema["maximum"]
- if "exclusiveMaximum" not in schema:
- maximum += 1
- return maximum
+ maximum = schema["maximum"]
+ if "exclusiveMaximum" not in schema:
+ maximum += 1
+ return maximum
- @base.generator_type("object")
- def gen_obj_remove_attr(self, schema):
- invalids = []
- valid_schema = valid.ValidTestGenerator().generate_valid(schema)
- required = schema.get("required", [])
- for r in required:
- new_valid = copy.deepcopy(valid_schema)
- del new_valid[r]
- invalids.append(("gen_obj_remove_attr", new_valid, None))
- return invalids
-
- @base.generator_type("object")
+ @base.generator_type("object", needed_property="additionalProperties")
@base.simple_generator
def gen_obj_add_attr(self, schema):
valid_schema = valid.ValidTestGenerator().generate_valid(schema)
- if not schema.get("additionalProperties", True):
- new_valid = copy.deepcopy(valid_schema)
- new_valid["$$$$$$$$$$"] = "xxx"
- return new_valid
-
- @base.generator_type("object")
- def gen_inv_prop_obj(self, schema):
- LOG.debug("generate_invalid_object: %s" % schema)
- valid_schema = valid.ValidTestGenerator().generate_valid(schema)
- invalids = []
- properties = schema["properties"]
-
- for k, v in properties.iteritems():
- for invalid in self.generate(v):
- LOG.debug(v)
- new_valid = copy.deepcopy(valid_schema)
- new_valid[k] = invalid[1]
- name = "prop_%s_%s" % (k, invalid[0])
- invalids.append((name, new_valid, invalid[2]))
-
- LOG.debug("generate_invalid_object return: %s" % invalids)
- return invalids
+ new_valid = copy.deepcopy(valid_schema)
+ new_valid["$$$$$$$$$$"] = "xxx"
+ return new_valid
diff --git a/tempest/common/generator/valid_generator.py b/tempest/common/generator/valid_generator.py
index 0d7b398..7b80afc 100644
--- a/tempest/common/generator/valid_generator.py
+++ b/tempest/common/generator/valid_generator.py
@@ -54,5 +54,28 @@
obj[k] = self.generate_valid(v)
return obj
+ def generate(self, schema):
+ schema_type = schema["type"]
+ if isinstance(schema_type, list):
+ if "integer" in schema_type:
+ schema_type = "integer"
+ else:
+ raise Exception("non-integer list types not supported")
+ result = []
+ if schema_type not in self.types_dict:
+ raise TypeError("generator (%s) doesn't support type: %s"
+ % (self.__class__.__name__, schema_type))
+ for generator in self.types_dict[schema_type]:
+ ret = generator(schema)
+ if ret is not None:
+ if isinstance(ret, list):
+ result.extend(ret)
+ elif isinstance(ret, tuple):
+ result.append(ret)
+ else:
+ raise Exception("generator (%s) returns invalid result: %s"
+ % (generator, ret))
+ return result
+
def generate_valid(self, schema):
return self.generate(schema)[0][1]
diff --git a/tempest/config.py b/tempest/config.py
index cea9dec..174a895 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -1141,8 +1141,10 @@
# to remove an issue with the config file up to date checker.
if parse_conf:
config_files.append(path)
-
- cfg.CONF([], project='tempest', default_config_files=config_files)
+ if os.path.isfile(path):
+ cfg.CONF([], project='tempest', default_config_files=config_files)
+ else:
+ cfg.CONF([], project='tempest')
logging.setup('tempest')
LOG = logging.getLogger('tempest')
LOG.info("Using tempest config file %s" % path)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index b3b4fbf..79207cd 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -49,8 +49,8 @@
"""Base class for scenario tests. Uses tempest own clients. """
@classmethod
- def setUpClass(cls):
- super(ScenarioTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(ScenarioTest, cls).resource_setup()
# Using tempest client for isolated credentials as well
cls.isolated_creds = isolated_creds.IsolatedCreds(
cls.__name__, network_resources=cls.network_resources)
@@ -474,8 +474,8 @@
raise cls.skipException('Neutron not available')
@classmethod
- def setUpClass(cls):
- super(NetworkScenarioTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(NetworkScenarioTest, cls).resource_setup()
cls.tenant_id = cls.manager.identity_client.tenant_id
cls.check_preconditions()
@@ -1002,8 +1002,8 @@
class BaremetalScenarioTest(ScenarioTest):
@classmethod
- def setUpClass(cls):
- super(BaremetalScenarioTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(BaremetalScenarioTest, cls).resource_setup()
if (not CONF.service_available.ironic or
not CONF.baremetal.driver_enabled):
@@ -1134,8 +1134,8 @@
"""
@classmethod
- def setUpClass(cls):
- super(EncryptionScenarioTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(EncryptionScenarioTest, cls).resource_setup()
cls.admin_volume_types_client = cls.admin_manager.volume_types_client
def _wait_for_volume_status(self, status):
@@ -1181,8 +1181,8 @@
"""
@classmethod
- def setUpClass(cls):
- super(OrchestrationScenarioTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(OrchestrationScenarioTest, cls).resource_setup()
if not CONF.service_available.heat:
raise cls.skipException("Heat support is required")
@@ -1226,9 +1226,9 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
cls.set_network_resources()
- super(SwiftScenarioTest, cls).setUpClass()
+ super(SwiftScenarioTest, cls).resource_setup()
if not CONF.service_available.swift:
skip_msg = ("%s skipped as swift is not available" %
cls.__name__)
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index 3ad5c69..75769ce 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -33,8 +33,8 @@
Deletes aggregate
"""
@classmethod
- def setUpClass(cls):
- super(TestAggregatesBasicOps, cls).setUpClass()
+ def resource_setup(cls):
+ super(TestAggregatesBasicOps, cls).resource_setup()
cls.aggregates_client = cls.manager.aggregates_client
cls.hosts_client = cls.manager.hosts_client
diff --git a/tempest/scenario/test_dashboard_basic_ops.py b/tempest/scenario/test_dashboard_basic_ops.py
index 4fcc70a..f218fb2 100644
--- a/tempest/scenario/test_dashboard_basic_ops.py
+++ b/tempest/scenario/test_dashboard_basic_ops.py
@@ -34,9 +34,9 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
cls.set_network_resources()
- super(TestDashboardBasicOps, cls).setUpClass()
+ super(TestDashboardBasicOps, cls).resource_setup()
if not CONF.service_available.horizon:
raise cls.skipException("Horizon support is required")
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
index 71b8a7f..b111939 100644
--- a/tempest/scenario/test_large_ops.py
+++ b/tempest/scenario/test_large_ops.py
@@ -38,12 +38,12 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
if CONF.scenario.large_ops_number < 1:
raise cls.skipException("large_ops_number not set to multiple "
"instances")
cls.set_network_resources()
- super(TestLargeOpsScenario, cls).setUpClass()
+ super(TestLargeOpsScenario, cls).resource_setup()
def _wait_for_server_status(self, status):
for server in self.servers:
diff --git a/tempest/scenario/test_load_balancer_basic.py b/tempest/scenario/test_load_balancer_basic.py
index 21680c2..9e404c8 100644
--- a/tempest/scenario/test_load_balancer_basic.py
+++ b/tempest/scenario/test_load_balancer_basic.py
@@ -57,8 +57,8 @@
raise cls.skipException(msg)
@classmethod
- def setUpClass(cls):
- super(TestLoadBalancerBasic, cls).setUpClass()
+ def resource_setup(cls):
+ super(TestLoadBalancerBasic, cls).resource_setup()
cls.check_preconditions()
cls.servers_keypairs = {}
cls.members = []
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 0277593..58a028f 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -50,10 +50,10 @@
raise cls.skipException(msg)
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
# Create no network resources for these tests.
cls.set_network_resources()
- super(TestNetworkAdvancedServerOps, cls).setUpClass()
+ super(TestNetworkAdvancedServerOps, cls).resource_setup()
def _setup_network_and_servers(self):
self.keypair = self.create_keypair()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 904f248..de60745 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -88,10 +88,10 @@
raise cls.skipException(msg)
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
# Create no network resources for these tests.
cls.set_network_resources()
- super(TestNetworkBasicOps, cls).setUpClass()
+ super(TestNetworkBasicOps, cls).resource_setup()
for ext in ['router', 'security-group']:
if not test.is_extension_enabled(ext, 'network'):
msg = "%s extension not enabled." % ext
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 658d336..188dea8 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -138,10 +138,10 @@
raise cls.skipException(msg)
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
# Create no network resources for these tests.
cls.set_network_resources()
- super(TestSecurityGroupsBasicOps, cls).setUpClass()
+ super(TestSecurityGroupsBasicOps, cls).resource_setup()
# TODO(mnewby) Consider looking up entities as needed instead
# of storing them as collections on the class.
cls.floating_ips = {}
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 463f5aa..c53e22b 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -35,9 +35,9 @@
"""
@classmethod
- def setUpClass(cls):
+ def resource_setup(cls):
cls.set_network_resources()
- super(TestServerAdvancedOps, cls).setUpClass()
+ super(TestServerAdvancedOps, cls).resource_setup()
if CONF.compute.flavor_ref_alt == CONF.compute.flavor_ref:
msg = "Skipping test - flavor_ref and flavor_ref_alt are identical"
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index b38b1a3..eb636f7 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -35,8 +35,7 @@
* Create a security group to control network access in instance
* Add simple permissive rules to the security group
* Launch an instance
- * Pause/unpause the instance
- * Suspend/resume the instance
+ * Perform ssh to instance
* Terminate the instance
"""
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index f2c3dcd..8ea2814 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -51,8 +51,8 @@
"""
@classmethod
- def setUpClass(cls):
- super(TestStampPattern, cls).setUpClass()
+ def resource_setup(cls):
+ super(TestStampPattern, cls).resource_setup()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index fdda423..a20db5c 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -36,8 +36,8 @@
* Check written content in the instance booted from snapshot
"""
@classmethod
- def setUpClass(cls):
- super(TestVolumeBootPattern, cls).setUpClass()
+ def resource_setup(cls):
+ super(TestVolumeBootPattern, cls).resource_setup()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
diff --git a/tempest/services/compute/json/images_client.py b/tempest/services/compute/json/images_client.py
index 9877391..4af8331 100644
--- a/tempest/services/compute/json/images_client.py
+++ b/tempest/services/compute/json/images_client.py
@@ -76,7 +76,7 @@
def get_image(self, image_id):
"""Returns the details of a single image."""
resp, body = self.get("images/%s" % str(image_id))
- self.expected_success(200, resp)
+ self.expected_success(200, resp.status)
body = json.loads(body)
self.validate_response(schema.get_image, resp, body)
return resp, body['image']
diff --git a/tempest/services/compute/xml/images_client.py b/tempest/services/compute/xml/images_client.py
index 6b15404..94acf36 100644
--- a/tempest/services/compute/xml/images_client.py
+++ b/tempest/services/compute/xml/images_client.py
@@ -127,7 +127,7 @@
def get_image(self, image_id):
"""Returns the details of a single image."""
resp, body = self.get("images/%s" % str(image_id))
- self.expected_success(200, resp)
+ self.expected_success(200, resp.status)
body = self._parse_image(etree.fromstring(body))
return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index 4a22b1b..adc1d37 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -123,7 +123,7 @@
def decorator(f):
services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
'network', 'identity', 'object_storage', 'dashboard',
- 'ceilometer', 'data_processing']
+ 'telemetry', 'data_processing']
for service in args:
if service not in services:
raise exceptions.InvalidServiceTag('%s is not a valid '
@@ -510,13 +510,9 @@
"expected_result": expected_result
}))
if schema is not None:
- for name, schema, expected_result in generator.generate(schema):
- if (expected_result is None and
- "default_result_code" in description):
- expected_result = description["default_result_code"]
- scenario_list.append((name,
- {"schema": schema,
- "expected_result": expected_result}))
+ for scenario in generator.generate_scenarios(schema):
+ scenario_list.append((scenario['_negtest_name'],
+ scenario))
LOG.debug(scenario_list)
return scenario_list
@@ -546,8 +542,14 @@
"""
LOG.info("Executing %s" % description["name"])
LOG.debug(description)
+ generator = importutils.import_class(
+ CONF.negative.test_generator)()
+ schema = description.get("json-schema", None)
method = description["http-method"]
url = description["url"]
+ expected_result = None
+ if "default_result_code" in description:
+ expected_result = description["default_result_code"]
resources = [self.get_resource(r) for
r in description.get("resources", [])]
@@ -557,13 +559,19 @@
# entry (see get_resource).
# We just send a valid json-schema with it
valid_schema = None
- schema = description.get("json-schema", None)
if schema:
valid_schema = \
valid.ValidTestGenerator().generate_valid(schema)
new_url, body = self._http_arguments(valid_schema, url, method)
- elif hasattr(self, "schema"):
- new_url, body = self._http_arguments(self.schema, url, method)
+ elif hasattr(self, "_negtest_name"):
+ schema_under_test = \
+ valid.ValidTestGenerator().generate_valid(schema)
+ local_expected_result = \
+ generator.generate_payload(self, schema_under_test)
+ if local_expected_result is not None:
+ expected_result = local_expected_result
+ new_url, body = \
+ self._http_arguments(schema_under_test, url, method)
else:
raise Exception("testscenarios are not active. Please make sure "
"that your test runner supports the load_tests "
@@ -575,7 +583,7 @@
client = self.client
resp, resp_body = client.send_request(method, new_url,
resources, body=body)
- self._check_negative_response(resp.status, resp_body)
+ self._check_negative_response(expected_result, resp.status, resp_body)
def _http_arguments(self, json_dict, url, method):
LOG.debug("dict: %s url: %s method: %s" % (json_dict, url, method))
@@ -586,8 +594,7 @@
else:
return url, json.dumps(json_dict)
- def _check_negative_response(self, result, body):
- expected_result = getattr(self, "expected_result", None)
+ def _check_negative_response(self, expected_result, result, body):
self.assertTrue(result >= 400 and result < 500 and result != 413,
"Expected client error, got %s:%s" %
(result, body))
diff --git a/tempest/tests/negative/test_negative_auto_test.py b/tempest/tests/negative/test_negative_auto_test.py
index dddd083..fb1da43 100644
--- a/tempest/tests/negative/test_negative_auto_test.py
+++ b/tempest/tests/negative/test_negative_auto_test.py
@@ -43,9 +43,9 @@
def _check_prop_entries(self, result, entry):
entries = [a for a in result if entry in a[0]]
self.assertIsNotNone(entries)
- self.assertIs(len(entries), 2)
+ self.assertGreater(len(entries), 1)
for entry in entries:
- self.assertIsNotNone(entry[1]['schema'])
+ self.assertIsNotNone(entry[1]['_negtest_name'])
def _check_resource_entries(self, result, entry):
entries = [a for a in result if entry in a[0]]
@@ -57,12 +57,11 @@
def test_generate_scenario(self):
scenarios = test.NegativeAutoTest.\
generate_scenario(self.fake_input_desc)
-
self.assertIsInstance(scenarios, list)
for scenario in scenarios:
self.assertIsInstance(scenario, tuple)
self.assertIsInstance(scenario[0], str)
self.assertIsInstance(scenario[1], dict)
- self._check_prop_entries(scenarios, "prop_minRam")
- self._check_prop_entries(scenarios, "prop_minDisk")
+ self._check_prop_entries(scenarios, "minRam")
+ self._check_prop_entries(scenarios, "minDisk")
self._check_resource_entries(scenarios, "inv_res")
diff --git a/tempest/tests/negative/test_negative_generators.py b/tempest/tests/negative/test_negative_generators.py
index a7af619..2fa6933 100644
--- a/tempest/tests/negative/test_negative_generators.py
+++ b/tempest/tests/negative/test_negative_generators.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
+
import jsonschema
import mock
@@ -86,15 +88,6 @@
class BaseNegativeGenerator(object):
types = ['string', 'integer', 'object']
- fake_input_str = {"type": "string",
- "minLength": 2,
- "maxLength": 8,
- 'results': {'gen_int': 404}}
-
- fake_input_int = {"type": "integer",
- "maximum": 255,
- "minimum": 1}
-
fake_input_obj = {"type": "object",
"properties": {"minRam": {"type": "integer"},
"diskName": {"type": "string"},
@@ -106,31 +99,21 @@
"type": "not_defined"
}
- def _validate_result(self, data):
- self.assertTrue(isinstance(data, list))
- for t in data:
- self.assertIsInstance(t, tuple)
- self.assertEqual(3, len(t))
- self.assertIsInstance(t[0], str)
+ class fake_test_class(object):
+ def __init__(self, scenario):
+ for k, v in scenario.iteritems():
+ setattr(self, k, v)
- def test_generate_string(self):
- result = self.generator.generate(self.fake_input_str)
- self._validate_result(result)
-
- def test_generate_integer(self):
- result = self.generator.generate(self.fake_input_int)
- self._validate_result(result)
-
- def test_generate_obj(self):
- result = self.generator.generate(self.fake_input_obj)
- self._validate_result(result)
+ def _validate_result(self, valid_schema, invalid_schema):
+ for k, v in valid_schema.iteritems():
+ self.assertTrue(k in invalid_schema)
def test_generator_mandatory_functions(self):
for data_type in self.types:
self.assertIn(data_type, self.generator.types_dict)
def test_generate_with_unknown_type(self):
- self.assertRaises(TypeError, self.generator.generate,
+ self.assertRaises(TypeError, self.generator.generate_payload,
self.unknown_type_schema)
@@ -151,3 +134,16 @@
def setUp(self):
super(TestNegativeNegativeGenerator, self).setUp()
self.generator = negative_generator.NegativeTestGenerator()
+
+ def test_generate_obj(self):
+ schema = self.fake_input_obj
+ scenarios = self.generator.generate_scenarios(schema)
+ for scenario in scenarios:
+ test = self.fake_test_class(scenario)
+ valid_schema = \
+ valid_generator.ValidTestGenerator().generate_valid(schema)
+ schema_under_test = copy.copy(valid_schema)
+ expected_result = \
+ self.generator.generate_payload(test, schema_under_test)
+ self.assertEqual(expected_result, None)
+ self._validate_result(valid_schema, schema_under_test)
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index 12104ec..32cefd0 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -97,6 +97,28 @@
self._test_services_helper, 'compute',
'volume')
+ def test_services_list(self):
+ service_list = test.get_service_list()
+ for service in service_list:
+ try:
+ self._test_services_helper(service)
+ except exceptions.InvalidServiceTag:
+ self.fail('%s is not listed in the valid service tag list'
+ % service)
+ except KeyError:
+ # NOTE(mtreinish): This condition is to test for a entry in
+ # the outer decorator list but not in the service_list dict.
+ # However, because we're looping over the service_list dict
+ # it's unlikely we'll trigger this. So manual review is still
+ # need for the list in the outer decorator.
+ self.fail('%s is in the list of valid service tags but there '
+ 'is no corresponding entry in the dict returned from'
+ ' get_service_list()' % service)
+ except testtools.TestCase.skipException:
+ # Test didn't raise an exception because of an incorrect list
+ # entry so move onto the next entry
+ continue
+
class TestStressDecorator(BaseDecoratorsTest):
def _test_stresstest_helper(self, expected_frequency='process',
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index f94d880..3496dce 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -195,8 +195,8 @@
"""Recommended to use as base class for boto related test."""
@classmethod
- def setUpClass(cls):
- super(BotoTestCase, cls).setUpClass()
+ def resource_setup(cls):
+ super(BotoTestCase, cls).resource_setup()
cls.conclusion = decision_maker()
cls.os = cls.get_client_manager()
# The trash contains cleanup functions and paramaters in tuples
@@ -245,7 +245,7 @@
raise self.failureException, "BotoServerError not raised"
@classmethod
- def tearDownClass(cls):
+ def resource_cleanup(cls):
"""Calls the callables added by addResourceCleanUp,
when you overwrite this function don't forget to call this too.
"""
@@ -264,7 +264,7 @@
finally:
del cls._resource_trash_bin[key]
cls.clear_isolated_creds()
- super(BotoTestCase, cls).tearDownClass()
+ super(BotoTestCase, cls).resource_cleanup()
# NOTE(afazekas): let the super called even on exceptions
# The real exceptions already logged, if the super throws another,
# does not causes hidden issues
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index c0d3f7a..f3f11fd 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -30,8 +30,8 @@
class InstanceRunTest(boto_test.BotoTestCase):
@classmethod
- def setUpClass(cls):
- super(InstanceRunTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(InstanceRunTest, cls).resource_setup()
if not cls.conclusion['A_I_IMAGES_READY']:
raise cls.skipException("".join(("EC2 ", cls.__name__,
": requires ami/aki/ari manifest")))
diff --git a/tempest/thirdparty/boto/test_ec2_keys.py b/tempest/thirdparty/boto/test_ec2_keys.py
index 698e3e1..c3e1e2a 100644
--- a/tempest/thirdparty/boto/test_ec2_keys.py
+++ b/tempest/thirdparty/boto/test_ec2_keys.py
@@ -26,8 +26,8 @@
class EC2KeysTest(boto_test.BotoTestCase):
@classmethod
- def setUpClass(cls):
- super(EC2KeysTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(EC2KeysTest, cls).resource_setup()
cls.client = cls.os.ec2api_client
cls.ec = cls.ec2_error_code
diff --git a/tempest/thirdparty/boto/test_ec2_network.py b/tempest/thirdparty/boto/test_ec2_network.py
index 792dde3..a75fb7b 100644
--- a/tempest/thirdparty/boto/test_ec2_network.py
+++ b/tempest/thirdparty/boto/test_ec2_network.py
@@ -20,8 +20,8 @@
class EC2NetworkTest(boto_test.BotoTestCase):
@classmethod
- def setUpClass(cls):
- super(EC2NetworkTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(EC2NetworkTest, cls).resource_setup()
cls.client = cls.os.ec2api_client
# Note(afazekas): these tests for things duable without an instance
diff --git a/tempest/thirdparty/boto/test_ec2_security_groups.py b/tempest/thirdparty/boto/test_ec2_security_groups.py
index 7d9bdab..fb3d32b 100644
--- a/tempest/thirdparty/boto/test_ec2_security_groups.py
+++ b/tempest/thirdparty/boto/test_ec2_security_groups.py
@@ -20,8 +20,8 @@
class EC2SecurityGroupTest(boto_test.BotoTestCase):
@classmethod
- def setUpClass(cls):
- super(EC2SecurityGroupTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(EC2SecurityGroupTest, cls).resource_setup()
cls.client = cls.os.ec2api_client
def test_create_authorize_security_group(self):
diff --git a/tempest/thirdparty/boto/test_ec2_volumes.py b/tempest/thirdparty/boto/test_ec2_volumes.py
index b50c6b0..9cee8a4 100644
--- a/tempest/thirdparty/boto/test_ec2_volumes.py
+++ b/tempest/thirdparty/boto/test_ec2_volumes.py
@@ -29,8 +29,8 @@
class EC2VolumesTest(boto_test.BotoTestCase):
@classmethod
- def setUpClass(cls):
- super(EC2VolumesTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(EC2VolumesTest, cls).resource_setup()
if not CONF.service_available.cinder:
skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
diff --git a/tempest/thirdparty/boto/test_s3_buckets.py b/tempest/thirdparty/boto/test_s3_buckets.py
index 1576492..342fc0e 100644
--- a/tempest/thirdparty/boto/test_s3_buckets.py
+++ b/tempest/thirdparty/boto/test_s3_buckets.py
@@ -20,8 +20,8 @@
class S3BucketsTest(boto_test.BotoTestCase):
@classmethod
- def setUpClass(cls):
- super(S3BucketsTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(S3BucketsTest, cls).resource_setup()
cls.client = cls.os.s3_client
def test_create_and_get_delete_bucket(self):
diff --git a/tempest/thirdparty/boto/test_s3_ec2_images.py b/tempest/thirdparty/boto/test_s3_ec2_images.py
index 389e25c..f5dec95 100644
--- a/tempest/thirdparty/boto/test_s3_ec2_images.py
+++ b/tempest/thirdparty/boto/test_s3_ec2_images.py
@@ -26,8 +26,8 @@
class S3ImagesTest(boto_test.BotoTestCase):
@classmethod
- def setUpClass(cls):
- super(S3ImagesTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(S3ImagesTest, cls).resource_setup()
if not cls.conclusion['A_I_IMAGES_READY']:
raise cls.skipException("".join(("EC2 ", cls.__name__,
": requires ami/aki/ari manifest")))
diff --git a/tempest/thirdparty/boto/test_s3_objects.py b/tempest/thirdparty/boto/test_s3_objects.py
index db3c1cf..43774c2 100644
--- a/tempest/thirdparty/boto/test_s3_objects.py
+++ b/tempest/thirdparty/boto/test_s3_objects.py
@@ -24,8 +24,8 @@
class S3BucketsTest(boto_test.BotoTestCase):
@classmethod
- def setUpClass(cls):
- super(S3BucketsTest, cls).setUpClass()
+ def resource_setup(cls):
+ super(S3BucketsTest, cls).resource_setup()
cls.client = cls.os.s3_client
def test_create_get_delete_object(self):