Merge "Enable H302 check (tempest/services/volume)"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index ee2da40..eb2340a 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -391,6 +391,20 @@
#endpoint_type=publicURL
+[database]
+
+#
+# Options defined in tempest.config
+#
+
+# Catalog type of the Database service. (string value)
+#catalog_type=database
+
+# Valid primary flavor to use in database tests. (string
+# value)
+#db_flavor_ref=1
+
+
[debug]
#
@@ -548,6 +562,16 @@
#ssh_user_regex=[["^.*[Cc]irros.*$", "root"]]
+[negative]
+
+#
+# Options defined in tempest.config
+#
+
+# Test generator class for all negative tests (string value)
+#test_generator=tempest.common.generator.negative_generator.NegativeTestGenerator
+
+
[network]
#
@@ -574,9 +598,6 @@
# The mask bits for tenant ipv4 subnets (integer value)
#tenant_network_mask_bits=28
-# Allow the execution of IPv6 tests (boolean value)
-#ipv6_enabled=true
-
# The cidr block to allocate tenant ipv6 subnets from (string
# value)
#tenant_network_v6_cidr=2003::/64
@@ -603,6 +624,9 @@
# Options defined in tempest.config
#
+# Allow the execution of IPv6 tests (boolean value)
+#ipv6=true
+
# A list of enabled network extensions with a special entry
# all which indicates every extension is enabled (list value)
#api_extensions=all
@@ -679,7 +703,7 @@
# Timeout in seconds to wait for a stack to build. (integer
# value)
-#build_timeout=300
+#build_timeout=600
# Instance type for tests. Needs to be big enough for a full
# OS plus the test workload (string value)
@@ -773,6 +797,10 @@
# value)
#ironic=false
+# Whether or not Trove is expected to be available (boolean
+# value)
+#trove=false
+
[stress]
diff --git a/tempest/api/database/__init__.py b/tempest/api/database/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/database/__init__.py
diff --git a/tempest/api/database/base.py b/tempest/api/database/base.py
new file mode 100644
index 0000000..8add9ba
--- /dev/null
+++ b/tempest/api/database/base.py
@@ -0,0 +1,42 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest import config
+from tempest.openstack.common import log as logging
+import tempest.test
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+class BaseDatabaseTest(tempest.test.BaseTestCase):
+ """Base test case class for all Database API tests."""
+
+ _interface = 'json'
+ force_tenant_isolation = False
+
+ @classmethod
+ def setUpClass(cls):
+ super(BaseDatabaseTest, cls).setUpClass()
+ if not CONF.service_available.trove:
+ skip_msg = ("%s skipped as trove is not available" % cls.__name__)
+ raise cls.skipException(skip_msg)
+
+ cls.catalog_type = CONF.database.catalog_type
+ cls.db_flavor_ref = CONF.database.db_flavor_ref
+
+ os = cls.get_client_manager()
+ cls.os = os
+ cls.database_flavors_client = cls.os.database_flavors_client
diff --git a/tempest/api/database/flavors/__init__.py b/tempest/api/database/flavors/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/database/flavors/__init__.py
diff --git a/tempest/api/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
new file mode 100644
index 0000000..a591e8e
--- /dev/null
+++ b/tempest/api/database/flavors/test_flavors.py
@@ -0,0 +1,41 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.database import base
+from tempest import test
+
+
+class DatabaseFlavorsTest(base.BaseDatabaseTest):
+
+ @classmethod
+ def setUpClass(cls):
+ super(DatabaseFlavorsTest, cls).setUpClass()
+ cls.client = cls.database_flavors_client
+
+ @test.attr(type='smoke')
+ def test_get_db_flavor(self):
+ # The expected flavor details should be returned
+ resp, flavor = self.client.get_db_flavor_details(self.db_flavor_ref)
+ self.assertEqual(self.db_flavor_ref, str(flavor['id']))
+ self.assertIn('ram', flavor)
+ self.assertIn('links', flavor)
+ self.assertIn('name', flavor)
+
+ @test.attr(type='smoke')
+ def test_list_db_flavors(self):
+ resp, flavor = self.client.get_db_flavor_details(self.db_flavor_ref)
+ # List of all flavors should contain the expected flavor
+ resp, flavors = self.client.list_db_flavors()
+ self.assertIn(flavor, flavors)
diff --git a/tempest/api/database/flavors/test_flavors_negative.py b/tempest/api/database/flavors/test_flavors_negative.py
new file mode 100644
index 0000000..202dc48
--- /dev/null
+++ b/tempest/api/database/flavors/test_flavors_negative.py
@@ -0,0 +1,32 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.database import base
+from tempest import exceptions
+from tempest import test
+
+
+class DatabaseFlavorsNegativeTest(base.BaseDatabaseTest):
+
+ @classmethod
+ def setUpClass(cls):
+ super(DatabaseFlavorsNegativeTest, cls).setUpClass()
+ cls.client = cls.database_flavors_client
+
+ @test.attr(type=['negative', 'gate'])
+ def test_get_non_existent_db_flavor(self):
+ # flavor details are not returned for non-existent flavors
+ self.assertRaises(exceptions.NotFound,
+ self.client.get_db_flavor_details, -1)
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index e6a078e..4cbb62f 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -14,12 +14,12 @@
from tempest.api.image import base
-from tempest.test import attr
+from tempest import test
class ImageMembersTest(base.BaseV1ImageMembersTest):
- @attr(type='gate')
+ @test.attr(type='gate')
def test_add_image_member(self):
image = self._create_image()
resp = self.client.add_member(self.alt_tenant_id, image)
@@ -33,7 +33,7 @@
resp, body = self.alt_img_cli.get_image(image)
self.assertEqual(200, resp.status)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_shared_images(self):
image = self._create_image()
resp = self.client.add_member(self.alt_tenant_id, image)
@@ -48,7 +48,7 @@
self.assertIn(share_image, images)
self.assertIn(image, images)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_remove_member(self):
image_id = self._create_image()
resp = self.client.add_member(self.alt_tenant_id, image_id)
diff --git a/tempest/api/image/v1/test_image_members_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index d68ef03..aac63b4 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -16,26 +16,26 @@
from tempest.api.image import base
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class ImageMembersNegativeTest(base.BaseV1ImageMembersTest):
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_add_member_with_non_existing_image(self):
# Add member with non existing image.
non_exist_image = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.add_member,
self.alt_tenant_id, non_exist_image)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_member_with_non_existing_image(self):
# Delete member with non existing image.
non_exist_image = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.delete_member,
self.alt_tenant_id, non_exist_image)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_member_with_non_existing_tenant(self):
# Delete member with non existing tenant.
image_id = self._create_image()
@@ -43,7 +43,7 @@
self.assertRaises(exceptions.NotFound, self.client.delete_member,
non_exist_tenant, image_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_get_image_without_membership(self):
# Image is hidden from another tenants.
image_id = self._create_image()
diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py
index 5695884..66556e0 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -15,30 +15,30 @@
from tempest.api.image import base
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class CreateDeleteImagesNegativeTest(base.BaseV1ImageTest):
"""Here are negative tests for the deletion and creation of images."""
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_register_with_invalid_container_format(self):
# Negative tests for invalid data supplied to POST /images
self.assertRaises(exceptions.BadRequest, self.client.create_image,
'test', 'wrong', 'vhd')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_register_with_invalid_disk_format(self):
self.assertRaises(exceptions.BadRequest, self.client.create_image,
'test', 'bare', 'wrong')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_image_with_invalid_image_id(self):
# An image should not be deleted with invalid image id
self.assertRaises(exceptions.NotFound, self.client.delete_image,
'!@$%^&*()')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_non_existent_image(self):
# Return an error while trying to delete a non-existent image
@@ -46,24 +46,24 @@
self.assertRaises(exceptions.NotFound, self.client.delete_image,
non_existent_image_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_image_blank_id(self):
# Return an error while trying to delete an image with blank Id
self.assertRaises(exceptions.NotFound, self.client.delete_image, '')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_image_non_hex_string_id(self):
# Return an error while trying to delete an image with non hex id
image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
self.assertRaises(exceptions.NotFound, self.client.delete_image,
image_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_image_negative_image_id(self):
# Return an error while trying to delete an image with negative id
self.assertRaises(exceptions.NotFound, self.client.delete_image, -1)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_image_id_is_over_35_character_limit(self):
# Return an error while trying to delete image with id over limit
self.assertRaises(exceptions.NotFound, self.client.delete_image,
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index d448c01..ce11911 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -19,7 +19,7 @@
from tempest.api.image import base
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class BasicOperationsImagesTest(base.BaseV2ImageTest):
@@ -27,7 +27,7 @@
Here we test the basic operations of images
"""
- @attr(type='gate')
+ @test.attr(type='gate')
def test_register_upload_get_image_file(self):
"""
@@ -68,7 +68,7 @@
self.assertEqual(200, resp.status)
self.assertEqual(file_content, body)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_delete_image(self):
# Deletes an image by image_id
@@ -90,7 +90,7 @@
self.assertEqual(resp.status, 200)
self.assertNotIn(image_id, images)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_update_image(self):
# Updates an image by image_id
@@ -176,7 +176,7 @@
msg = "Failed to list images by %s" % key
self.assertEqual(params[key], image[key], msg)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_index_no_params(self):
# Simple test to see all fixture images returned
resp, images_list = self.client.image_list()
@@ -186,25 +186,25 @@
for image in self.created_images:
self.assertIn(image, image_list)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_images_param_container_format(self):
# Test to get all images with container_format='bare'
params = {"container_format": "bare"}
self._list_by_param_value_and_assert(params)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_images_param_disk_format(self):
# Test to get all images with disk_format = raw
params = {"disk_format": "raw"}
self._list_by_param_value_and_assert(params)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_images_param_visibility(self):
# Test to get all images with visibility = public
params = {"visibility": "public"}
self._list_by_param_value_and_assert(params)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_images_param_size(self):
# Test to get all images by size
image_id = self.created_images[1]
@@ -215,7 +215,7 @@
params = {"size": image['size']}
self._list_by_param_value_and_assert(params)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_images_param_min_max_size(self):
# Test to get all images with size between 2000 to 3000
image_id = self.created_images[1]
@@ -234,13 +234,13 @@
image_size <= params['size_max'],
"Failed to get images by size_min and size_max")
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_images_param_status(self):
# Test to get all active images
params = {"status": "active"}
self._list_by_param_value_and_assert(params)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_images_param_limit(self):
# Test to get images by limit
params = {"limit": 2}
@@ -250,7 +250,7 @@
self.assertEqual(len(images_list), params['limit'],
"Failed to get images by limit")
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_image_schema(self):
# Test to get image schema
schema = "image"
@@ -258,7 +258,7 @@
self.assertEqual(200, resp.status)
self.assertEqual("image", body['name'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_images_schema(self):
# Test to get images schema
schema = "images"
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index 530262f..e6c5b61 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -11,13 +11,13 @@
# under the License.
from tempest.api.image import base
-from tempest.test import attr
+from tempest import test
class ImagesMemberTest(base.BaseV2MemberImageTest):
_interface = 'json'
- @attr(type='gate')
+ @test.attr(type='gate')
def test_image_share_accept(self):
image_id = self._create_image()
resp, member = self.os_img_client.add_member(image_id,
@@ -40,7 +40,7 @@
self.assertEqual(member['image_id'], image_id)
self.assertEqual(member['status'], 'accepted')
- @attr(type='gate')
+ @test.attr(type='gate')
def test_image_share_reject(self):
image_id = self._create_image()
resp, member = self.os_img_client.add_member(image_id,
@@ -56,7 +56,7 @@
self.assertEqual(200, resp.status)
self.assertNotIn(image_id, self._list_image_ids_as_alt())
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_image_member(self):
image_id = self._create_image()
self.os_img_client.add_member(image_id,
@@ -73,7 +73,7 @@
self.assertEqual(image_id, member['image_id'])
self.assertEqual('accepted', member['status'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_remove_image_member(self):
image_id = self._create_image()
self.os_img_client.add_member(image_id,
diff --git a/tempest/api/image/v2/test_images_member_negative.py b/tempest/api/image/v2/test_images_member_negative.py
index 4c7cc5a..98ef649 100644
--- a/tempest/api/image/v2/test_images_member_negative.py
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -12,13 +12,13 @@
from tempest.api.image import base
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class ImagesMemberNegativeTest(base.BaseV2MemberImageTest):
_interface = 'json'
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_image_share_invalid_status(self):
image_id = self._create_image()
resp, member = self.os_img_client.add_member(image_id,
@@ -28,7 +28,7 @@
self.alt_img_client.update_member_status,
image_id, self.alt_tenant_id, 'notavalidstatus')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_image_share_owner_cannot_accept(self):
image_id = self._create_image()
resp, member = self.os_img_client.add_member(image_id,
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index b8ba868..27ba39c 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -18,7 +18,7 @@
from tempest.api.image import base
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class ImagesNegativeTest(base.BaseV2ImageTest):
@@ -35,20 +35,20 @@
** delete the deleted image
"""
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_get_non_existent_image(self):
# get the non-existent image
non_existent_id = str(uuid.uuid4())
self.assertRaises(exceptions.NotFound, self.client.get_image,
non_existent_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_get_image_null_id(self):
# get image with image_id = NULL
image_id = ""
self.assertRaises(exceptions.NotFound, self.client.get_image, image_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_get_delete_deleted_image(self):
# get and delete the deleted image
# create and delete image
@@ -67,27 +67,27 @@
self.assertRaises(exceptions.NotFound, self.client.delete_image,
image_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_non_existing_image(self):
# delete non-existent image
non_existent_image_id = str(uuid.uuid4())
self.assertRaises(exceptions.NotFound, self.client.delete_image,
non_existent_image_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_image_null_id(self):
# delete image with image_id=NULL
image_id = ""
self.assertRaises(exceptions.NotFound, self.client.delete_image,
image_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_register_with_invalid_container_format(self):
# Negative tests for invalid data supplied to POST /images
self.assertRaises(exceptions.BadRequest, self.client.create_image,
'test', 'wrong', 'vhd')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_register_with_invalid_disk_format(self):
self.assertRaises(exceptions.BadRequest, self.client.create_image,
'test', 'bare', 'wrong')
diff --git a/tempest/api/image/v2/test_images_tags.py b/tempest/api/image/v2/test_images_tags.py
index f0e343d..504c0e8 100644
--- a/tempest/api/image/v2/test_images_tags.py
+++ b/tempest/api/image/v2/test_images_tags.py
@@ -14,12 +14,12 @@
from tempest.api.image import base
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class ImagesTagsTest(base.BaseV2ImageTest):
- @attr(type='gate')
+ @test.attr(type='gate')
def test_update_delete_tags_for_image(self):
resp, body = self.create_image(container_format='bare',
disk_format='raw',
diff --git a/tempest/api/image/v2/test_images_tags_negative.py b/tempest/api/image/v2/test_images_tags_negative.py
index 0628d29..3233db7 100644
--- a/tempest/api/image/v2/test_images_tags_negative.py
+++ b/tempest/api/image/v2/test_images_tags_negative.py
@@ -17,12 +17,12 @@
from tempest.api.image import base
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class ImagesTagsNegativeTest(base.BaseV2ImageTest):
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_tags_for_non_existing_image(self):
# Update tag with non existing image.
tag = data_utils.rand_name('tag-')
@@ -30,7 +30,7 @@
self.assertRaises(exceptions.NotFound, self.client.add_image_tag,
non_exist_image, tag)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_non_existing_tag(self):
# Delete non existing tag.
resp, body = self.create_image(container_format='bare',
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index ba6a617..93335e7 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -59,7 +59,6 @@
# Create no network resources for these test.
cls.set_network_resources()
super(BaseNetworkTest, cls).setUpClass()
- os = clients.Manager(interface=cls._interface)
if not CONF.service_available.neutron:
raise cls.skipException("Neutron support is required")
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index ea802ad..1155257 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -455,7 +455,7 @@
@classmethod
def setUpClass(cls):
super(NetworksIpV6TestJSON, cls).setUpClass()
- if not CONF.network.ipv6_enabled:
+ if not CONF.network_feature_enabled.ipv6:
cls.tearDownClass()
skip_msg = "IPv6 Tests are disabled."
raise cls.skipException(skip_msg)
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index 291f0d1..18ba37b 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -73,7 +73,7 @@
subnet_id: {get_resource: Subnet}
Server:
type: AWS::EC2::Instance
- Metadata:
+ metadata:
Name: SmokeServerNeutron
properties:
ImageId: {get_param: ImageId}
@@ -93,7 +93,7 @@
type: AWS::CloudFormation::WaitConditionHandle
WaitCondition:
type: AWS::CloudFormation::WaitCondition
- DependsOn: Server
+ depends_on: Server
properties:
Handle: {get_resource: WaitHandleNeutron}
Timeout: '600'
diff --git a/tempest/api/orchestration/stacks/test_server_cfn_init.py b/tempest/api/orchestration/stacks/test_server_cfn_init.py
index 5130f87..7e8bc2d 100644
--- a/tempest/api/orchestration/stacks/test_server_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_server_cfn_init.py
@@ -154,8 +154,8 @@
sid = self.stack_identifier
rid = 'SmokeServer'
- # wait for server resource create to complete.
- self.client.wait_for_resource_status(sid, rid, 'CREATE_COMPLETE')
+ # wait for create to complete.
+ self.client.wait_for_stack_status(sid, 'CREATE_COMPLETE')
resp, body = self.client.get_resource(sid, rid)
self.assertEqual('CREATE_COMPLETE', body['resource_status'])
diff --git a/tempest/api/orchestration/stacks/test_swift_resources.py b/tempest/api/orchestration/stacks/test_swift_resources.py
index 5921a7a..713cfd4 100644
--- a/tempest/api/orchestration/stacks/test_swift_resources.py
+++ b/tempest/api/orchestration/stacks/test_swift_resources.py
@@ -27,20 +27,20 @@
_interface = 'json'
template = """
heat_template_version: 2013-05-23
-description: Template which creates a Swift container ressource
+description: Template which creates a Swift container resource
resources:
SwiftContainerWebsite:
- DeletionPolicy: "Delete"
- Type: OS::Swift::Container
- Properties:
+ deletion_policy: "Delete"
+ type: OS::Swift::Container
+ properties:
X-Container-Read: ".r:*"
X-Container-Meta:
web-index: "index.html"
web-error: "error.html"
SwiftContainer:
- Type: OS::Swift::Container
+ type: OS::Swift::Container
outputs:
WebsiteURL:
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index b0c878b..6178a1c 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -14,7 +14,7 @@
from tempest.common.utils import data_utils
from tempest import config
from tempest.openstack.common import log as logging
-from tempest.test import attr
+from tempest import test
CONF = config.CONF
@@ -86,7 +86,7 @@
super(VolumeMultiBackendTest, cls).tearDownClass()
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_backend_name_reporting(self):
# this test checks if os-vol-attr:host is populated correctly after
# the multi backend feature has been enabled
@@ -100,7 +100,7 @@
self.volume1['id'])
self.assertTrue(len(volume1_host.split("@")) > 1, msg)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_backend_name_distinction(self):
# this test checks that the two volumes created at setUp don't
# belong to the same backend (if they are, than the
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 12fda92..e140ad0 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -15,7 +15,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class SnapshotsActionsTest(base.BaseVolumeV1AdminTest):
@@ -80,7 +80,7 @@
def _get_progress_alias(self):
return 'os-extended-snapshot-attributes:progress'
- @attr(type='gate')
+ @test.attr(type='gate')
def test_reset_snapshot_status(self):
# Reset snapshot status to creating
status = 'creating'
@@ -92,7 +92,7 @@
self.assertEqual(200, resp_get.status)
self.assertEqual(status, snapshot_get['status'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_update_snapshot_status(self):
# Reset snapshot status to creating
status = 'creating'
@@ -112,22 +112,22 @@
self.assertEqual(status, snapshot_get['status'])
self.assertEqual(progress, snapshot_get[progress_alias])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_snapshot_force_delete_when_snapshot_is_creating(self):
# test force delete when status of snapshot is creating
self._create_reset_and_force_delete_temp_snapshot('creating')
- @attr(type='gate')
+ @test.attr(type='gate')
def test_snapshot_force_delete_when_snapshot_is_deleting(self):
# test force delete when status of snapshot is deleting
self._create_reset_and_force_delete_temp_snapshot('deleting')
- @attr(type='gate')
+ @test.attr(type='gate')
def test_snapshot_force_delete_when_snapshot_is_error(self):
# test force delete when status of snapshot is error
self._create_reset_and_force_delete_temp_snapshot('error')
- @attr(type='gate')
+ @test.attr(type='gate')
def test_snapshot_force_delete_when_snapshot_is_error_deleting(self):
# test force delete when status of snapshot is error_deleting
self._create_reset_and_force_delete_temp_snapshot('error_deleting')
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index 5c311e1..01ba915 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -14,13 +14,13 @@
# under the License.
from tempest.api.volume import base
-from tempest.test import attr
+from tempest import test
class VolumeHostsAdminTestsJSON(base.BaseVolumeV1AdminTest):
_interface = "json"
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_hosts(self):
resp, hosts = self.hosts_client.list_hosts()
self.assertEqual(200, resp.status)
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index d481251..8183999 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -16,7 +16,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest import config
-from tempest.test import attr
+from tempest import test
CONF = config.CONF
@@ -33,14 +33,14 @@
resp, _ = self.client.delete_volume_type(volume_type_id)
self.assertEqual(202, resp.status)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_volume_type_list(self):
# List Volume types.
resp, body = self.client.list_volume_types()
self.assertEqual(200, resp.status)
self.assertIsInstance(body, list)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_get_delete_volume_with_volume_type_and_extra_specs(self):
# Create/get/delete volume with volume_type and extra spec.
volume = {}
@@ -84,7 +84,7 @@
'The fetched Volume is different '
'from the created Volume')
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_volume_type_create_get_delete(self):
# Create/get volume type.
body = {}
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 99a0826..06a0b34 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -15,7 +15,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class VolumeTypesExtraSpecsTest(base.BaseVolumeV1AdminTest):
@@ -32,7 +32,7 @@
cls.client.delete_volume_type(cls.volume_type['id'])
super(VolumeTypesExtraSpecsTest, cls).tearDownClass()
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_volume_type_extra_specs_list(self):
# List Volume types extra specs.
extra_specs = {"spec1": "val1"}
@@ -47,7 +47,7 @@
self.assertIsInstance(body, dict)
self.assertIn('spec1', body)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_type_extra_specs_update(self):
# Update volume type extra specs
extra_specs = {"spec2": "val1"}
@@ -67,7 +67,7 @@
self.assertEqual(extra_spec['spec2'], body['spec2'],
"Volume type extra spec incorrectly updated")
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_volume_type_extra_spec_create_get_delete(self):
# Create/Get/Delete volume type extra spec.
extra_specs = {"spec3": "val1"}
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 5a1a2cd..d3a052e 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
@@ -18,7 +18,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class ExtraSpecsNegativeTest(base.BaseVolumeV1AdminTest):
@@ -38,7 +38,7 @@
cls.client.delete_volume_type(cls.volume_type['id'])
super(ExtraSpecsNegativeTest, cls).tearDownClass()
- @attr(type='gate')
+ @test.attr(type='gate')
def test_update_no_body(self):
# Should not update volume type extra specs with no body
extra_spec = {"spec1": "val2"}
@@ -46,7 +46,7 @@
self.client.update_volume_type_extra_specs,
self.volume_type['id'], extra_spec.keys()[0], None)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_update_nonexistent_extra_spec_id(self):
# Should not update volume type extra specs with nonexistent id.
extra_spec = {"spec1": "val2"}
@@ -55,7 +55,7 @@
self.volume_type['id'], str(uuid.uuid4()),
extra_spec)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_update_none_extra_spec_id(self):
# Should not update volume type extra specs with none id.
extra_spec = {"spec1": "val2"}
@@ -63,7 +63,7 @@
self.client.update_volume_type_extra_specs,
self.volume_type['id'], None, extra_spec)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_update_multiple_extra_spec(self):
# Should not update volume type extra specs with multiple specs as
# body.
@@ -73,7 +73,7 @@
self.volume_type['id'], extra_spec.keys()[0],
extra_spec)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_create_nonexistent_type_id(self):
# Should not create volume type extra spec for nonexistent volume
# type id.
@@ -82,21 +82,21 @@
self.client.create_volume_type_extra_specs,
str(uuid.uuid4()), extra_specs)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_create_none_body(self):
# Should not create volume type extra spec for none POST body.
self.assertRaises(exceptions.BadRequest,
self.client.create_volume_type_extra_specs,
self.volume_type['id'], None)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_create_invalid_body(self):
# Should not create volume type extra spec for invalid POST body.
self.assertRaises(exceptions.BadRequest,
self.client.create_volume_type_extra_specs,
self.volume_type['id'], ['invalid'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_delete_nonexistent_volume_type_id(self):
# Should not delete volume type extra spec for nonexistent
# type id.
@@ -105,14 +105,14 @@
self.client.delete_volume_type_extra_specs,
str(uuid.uuid4()), extra_specs.keys()[0])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_nonexistent_volume_type_id(self):
# Should not list volume type extra spec for nonexistent type id.
self.assertRaises(exceptions.NotFound,
self.client.list_volume_types_extra_specs,
str(uuid.uuid4()))
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_nonexistent_volume_type_id(self):
# Should not get volume type extra spec for nonexistent type id.
extra_specs = {"spec1": "val1"}
@@ -120,7 +120,7 @@
self.client.get_volume_type_extra_specs,
str(uuid.uuid4()), extra_specs.keys()[0])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_nonexistent_extra_spec_id(self):
# Should not get volume type extra spec for nonexistent extra spec
# id.
diff --git a/tempest/api/volume/admin/test_volume_types_negative.py b/tempest/api/volume/admin/test_volume_types_negative.py
index 56ad227..c18e15d 100644
--- a/tempest/api/volume/admin/test_volume_types_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_negative.py
@@ -17,13 +17,13 @@
from tempest.api.volume import base
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class VolumeTypesNegativeTest(base.BaseVolumeV1AdminTest):
_interface = 'json'
- @attr(type='gate')
+ @test.attr(type='gate')
def test_create_with_nonexistent_volume_type(self):
# Should not be able to create volume with nonexistent volume_type.
self.assertRaises(exceptions.NotFound,
@@ -31,19 +31,19 @@
display_name=str(uuid.uuid4()),
volume_type=str(uuid.uuid4()))
- @attr(type='gate')
+ @test.attr(type='gate')
def test_create_with_empty_name(self):
# Should not be able to create volume type with an empty name.
self.assertRaises(exceptions.BadRequest,
self.client.create_volume_type, '')
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_nonexistent_type_id(self):
# Should not be able to get volume type with nonexistent type id.
self.assertRaises(exceptions.NotFound, self.client.get_volume_type,
str(uuid.uuid4()))
- @attr(type='gate')
+ @test.attr(type='gate')
def test_delete_nonexistent_type_id(self):
# Should not be able to delete volume type with nonexistent type id.
self.assertRaises(exceptions.NotFound, self.client.delete_volume_type,
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 9274fce..aa00700 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -15,7 +15,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils as utils
-from tempest.test import attr
+from tempest import test
class VolumesActionsTest(base.BaseVolumeV1AdminTest):
@@ -75,7 +75,7 @@
self.assertEqual(202, resp_delete.status)
self.client.wait_for_resource_deletion(temp_volume['id'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_reset_status(self):
# test volume reset status : available->error->available
resp, body = self._reset_volume_status(self.volume['id'], 'error')
@@ -84,7 +84,7 @@
self.volume['id'])
self.assertEqual('error', volume_get['status'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_begin_detaching(self):
# test volume begin detaching : available -> detaching -> available
resp, body = self.client.volume_begin_detaching(self.volume['id'])
@@ -92,7 +92,7 @@
resp_get, volume_get = self.client.get_volume(self.volume['id'])
self.assertEqual('detaching', volume_get['status'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_roll_detaching(self):
# test volume roll detaching : detaching -> in-use -> available
resp, body = self.client.volume_begin_detaching(self.volume['id'])
@@ -110,7 +110,7 @@
# test force delete when status of volume is attaching
self._create_reset_and_force_delete_temp_volume('attaching')
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_force_delete_when_volume_is_error(self):
# test force delete when status of volume is error
self._create_reset_and_force_delete_temp_volume('error')
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 47094f0..cd6d7a8 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api.volume.base import BaseVolumeV1AdminTest
+from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest import config
from tempest.openstack.common import log as logging
@@ -23,7 +23,7 @@
LOG = logging.getLogger(__name__)
-class VolumesBackupsTest(BaseVolumeV1AdminTest):
+class VolumesBackupsTest(base.BaseVolumeV1AdminTest):
_interface = "json"
@classmethod
@@ -38,7 +38,8 @@
cls.volume = cls.create_volume()
@test.attr(type='smoke')
- def test_volume_backup_create_get_restore_delete(self):
+ def test_volume_backup_create_get_detailed_list_restore_delete(self):
+ # Create backup
backup_name = data_utils.rand_name('Backup')
create_backup = self.backups_adm_client.create_backup
resp, backup = create_backup(self.volume['id'],
@@ -46,21 +47,31 @@
self.assertEqual(202, resp.status)
self.addCleanup(self.backups_adm_client.delete_backup,
backup['id'])
- self.assertEqual(backup['name'], backup_name)
+ self.assertEqual(backup_name, backup['name'])
self.volumes_adm_client.wait_for_volume_status(self.volume['id'],
'available')
self.backups_adm_client.wait_for_backup_status(backup['id'],
'available')
+ # Get a given backup
resp, backup = self.backups_adm_client.get_backup(backup['id'])
self.assertEqual(200, resp.status)
- self.assertEqual(backup['name'], backup_name)
+ self.assertEqual(backup_name, backup['name'])
+ # Get all backups with detail
+ resp, backups = self.backups_adm_client.list_backups_with_detail()
+ self.assertEqual(200, resp.status)
+ self.assertIn((backup['name'], backup['id']),
+ [(m['name'], m['id']) for m in backups])
+
+ # Restore backup
resp, restore = self.backups_adm_client.restore_backup(backup['id'])
self.assertEqual(202, resp.status)
+
+ # Delete backup
self.addCleanup(self.volumes_adm_client.delete_volume,
restore['volume_id'])
- self.assertEqual(restore['backup_id'], backup['id'])
+ self.assertEqual(backup['id'], restore['backup_id'])
self.backups_adm_client.wait_for_backup_status(backup['id'],
'available')
self.volumes_adm_client.wait_for_volume_status(restore['volume_id'],
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
index cceffd6..ce019a2 100644
--- a/tempest/api/volume/test_extensions.py
+++ b/tempest/api/volume/test_extensions.py
@@ -17,7 +17,7 @@
from tempest.api.volume import base
from tempest import config
from tempest.openstack.common import log as logging
-from tempest.test import attr
+from tempest import test
CONF = config.CONF
@@ -28,7 +28,7 @@
class ExtensionsTestJSON(base.BaseVolumeV1Test):
_interface = 'json'
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_extensions(self):
# List of all extensions
resp, extensions = self.volumes_extension_client.list_extensions()
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index f4b2d4c..55a72c1 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -18,7 +18,7 @@
from tempest.api.volume import base
from tempest import clients
from tempest import config
-from tempest.test import attr
+from tempest import test
CONF = config.CONF
@@ -66,7 +66,7 @@
self.assertEqual(202, resp.status)
self.adm_client.wait_for_resource_deletion(volume_id)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_create_get_list_accept_volume_transfer(self):
# Create a volume first
volume = self.create_volume()
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 284c321..82924a5 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -18,7 +18,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class VolumesNegativeTest(base.BaseVolumeV1Test):
@@ -33,19 +33,19 @@
cls.volume = cls.create_volume()
cls.mountpoint = "/dev/vdc"
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_volume_get_nonexistent_volume_id(self):
# Should not be able to get a non-existent volume
self.assertRaises(exceptions.NotFound, self.client.get_volume,
str(uuid.uuid4()))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_volume_delete_nonexistent_volume_id(self):
# Should not be able to delete a non-existent Volume
self.assertRaises(exceptions.NotFound, self.client.delete_volume,
str(uuid.uuid4()))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_volume_with_invalid_size(self):
# Should not be able to create volume with invalid size
# in request
@@ -54,7 +54,7 @@
self.assertRaises(exceptions.BadRequest, self.client.create_volume,
size='#$%', display_name=v_name, metadata=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_volume_with_out_passing_size(self):
# Should not be able to create volume without passing size
# in request
@@ -63,7 +63,7 @@
self.assertRaises(exceptions.BadRequest, self.client.create_volume,
size='', display_name=v_name, metadata=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_volume_with_size_zero(self):
# Should not be able to create volume with size zero
v_name = data_utils.rand_name('Volume-')
@@ -71,7 +71,7 @@
self.assertRaises(exceptions.BadRequest, self.client.create_volume,
size='0', display_name=v_name, metadata=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_volume_with_size_negative(self):
# Should not be able to create volume with size negative
v_name = data_utils.rand_name('Volume-')
@@ -79,7 +79,7 @@
self.assertRaises(exceptions.BadRequest, self.client.create_volume,
size='-1', display_name=v_name, metadata=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_volume_with_nonexistent_volume_type(self):
# Should not be able to create volume with non-existent volume type
v_name = data_utils.rand_name('Volume-')
@@ -88,7 +88,7 @@
size='1', volume_type=str(uuid.uuid4()),
display_name=v_name, metadata=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_volume_with_nonexistent_snapshot_id(self):
# Should not be able to create volume with non-existent snapshot
v_name = data_utils.rand_name('Volume-')
@@ -97,7 +97,7 @@
size='1', snapshot_id=str(uuid.uuid4()),
display_name=v_name, metadata=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_volume_with_nonexistent_source_volid(self):
# Should not be able to create volume with non-existent source volume
v_name = data_utils.rand_name('Volume-')
@@ -106,7 +106,7 @@
size='1', source_volid=str(uuid.uuid4()),
display_name=v_name, metadata=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_volume_with_nonexistent_volume_id(self):
v_name = data_utils.rand_name('Volume-')
metadata = {'Type': 'work'}
@@ -114,7 +114,7 @@
volume_id=str(uuid.uuid4()), display_name=v_name,
metadata=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_volume_with_invalid_volume_id(self):
v_name = data_utils.rand_name('Volume-')
metadata = {'Type': 'work'}
@@ -122,7 +122,7 @@
volume_id='#$%%&^&^', display_name=v_name,
metadata=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_volume_with_empty_volume_id(self):
v_name = data_utils.rand_name('Volume-')
metadata = {'Type': 'work'}
@@ -130,29 +130,29 @@
volume_id='', display_name=v_name,
metadata=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_get_invalid_volume_id(self):
# Should not be able to get volume with invalid id
self.assertRaises(exceptions.NotFound, self.client.get_volume,
'#$%%&^&^')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_get_volume_without_passing_volume_id(self):
# Should not be able to get volume when empty ID is passed
self.assertRaises(exceptions.NotFound, self.client.get_volume, '')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_invalid_volume_id(self):
# Should not be able to delete volume when invalid ID is passed
self.assertRaises(exceptions.NotFound, self.client.delete_volume,
'!@#$%^&*()')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_volume_without_passing_volume_id(self):
# Should not be able to delete volume when empty ID is passed
self.assertRaises(exceptions.NotFound, self.client.delete_volume, '')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_attach_volumes_with_nonexistent_volume_id(self):
srv_name = data_utils.rand_name('Instance-')
resp, server = self.servers_client.create_server(srv_name,
@@ -166,60 +166,60 @@
server['id'],
self.mountpoint)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_detach_volumes_with_invalid_volume_id(self):
self.assertRaises(exceptions.NotFound,
self.client.detach_volume,
'xxx')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_volume_extend_with_size_smaller_than_original_size(self):
# Extend volume with smaller size than original size.
extend_size = 0
self.assertRaises(exceptions.BadRequest, self.client.extend_volume,
self.volume['id'], extend_size)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_volume_extend_with_non_number_size(self):
# Extend volume when size is non number.
extend_size = 'abc'
self.assertRaises(exceptions.BadRequest, self.client.extend_volume,
self.volume['id'], extend_size)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_volume_extend_with_None_size(self):
# Extend volume with None size.
extend_size = None
self.assertRaises(exceptions.BadRequest, self.client.extend_volume,
self.volume['id'], extend_size)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_volume_extend_with_nonexistent_volume_id(self):
# Extend volume size when volume is nonexistent.
extend_size = int(self.volume['size']) + 1
self.assertRaises(exceptions.NotFound, self.client.extend_volume,
str(uuid.uuid4()), extend_size)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_volume_extend_without_passing_volume_id(self):
# Extend volume size when passing volume id is None.
extend_size = int(self.volume['size']) + 1
self.assertRaises(exceptions.NotFound, self.client.extend_volume,
None, extend_size)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_reserve_volume_with_nonexistent_volume_id(self):
self.assertRaises(exceptions.NotFound,
self.client.reserve_volume,
str(uuid.uuid4()))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_unreserve_volume_with_nonexistent_volume_id(self):
self.assertRaises(exceptions.NotFound,
self.client.unreserve_volume,
str(uuid.uuid4()))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_reserve_volume_with_negative_volume_status(self):
# Mark volume as reserved.
resp, body = self.client.reserve_volume(self.volume['id'])
@@ -232,7 +232,7 @@
resp, body = self.client.unreserve_volume(self.volume['id'])
self.assertEqual(202, resp.status)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_volumes_with_nonexistent_name(self):
v_name = data_utils.rand_name('Volume-')
params = {'display_name': v_name}
@@ -240,7 +240,7 @@
self.assertEqual(200, resp.status)
self.assertEqual(0, len(fetched_volume))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_volumes_detail_with_nonexistent_name(self):
v_name = data_utils.rand_name('Volume-')
params = {'display_name': v_name}
@@ -248,14 +248,14 @@
self.assertEqual(200, resp.status)
self.assertEqual(0, len(fetched_volume))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_volumes_with_invalid_status(self):
params = {'status': 'null'}
resp, fetched_volume = self.client.list_volumes(params)
self.assertEqual(200, resp.status)
self.assertEqual(0, len(fetched_volume))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_volumes_detail_with_invalid_status(self):
params = {'status': 'null'}
resp, fetched_volume = self.client.list_volumes_with_detail(params)
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 56915e6..2701e84 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -14,7 +14,7 @@
from tempest.common.utils import data_utils
from tempest import config
from tempest.openstack.common import log as logging
-from tempest.test import attr
+from tempest import test
LOG = logging.getLogger(__name__)
CONF = config.CONF
@@ -63,7 +63,7 @@
('details' if with_detail else '', key)
self.assertEqual(params[key], snap[key], msg)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_snapshot_create_with_volume_in_use(self):
# Create a snapshot when volume status is in-use
# Create a test instance
@@ -89,7 +89,7 @@
self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
self.snapshots.remove(snapshot)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_snapshot_create_get_list_update_delete(self):
# Create a snapshot
s_name = data_utils.rand_name('snap')
@@ -134,7 +134,7 @@
self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
self.snapshots.remove(snapshot)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_snapshots_list_with_params(self):
"""list snapshots with params."""
# Create a snapshot
@@ -155,7 +155,7 @@
'display_name': snapshot['display_name']}
self._list_by_param_values_and_assert(params)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_snapshots_list_details_with_params(self):
"""list snapshot details with params."""
# Create a snapshot
@@ -174,7 +174,7 @@
'display_name': snapshot['display_name']}
self._list_by_param_values_and_assert(params, with_detail=True)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_from_snapshot(self):
# Create a temporary snap using wrapper method from base, then
# create a snap based volume, check resp code and deletes it
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index b24b597..9e47c03 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -15,13 +15,13 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class VolumesSnapshotNegativeTest(base.BaseVolumeV1Test):
_interface = "json"
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_snapshot_with_nonexistent_volume_id(self):
# Create a snapshot with nonexistent volume id
s_name = data_utils.rand_name('snap')
@@ -29,7 +29,7 @@
self.snapshots_client.create_snapshot,
str(uuid.uuid4()), display_name=s_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_snapshot_without_passing_volume_id(self):
# Create a snapshot without passing volume id
s_name = data_utils.rand_name('snap')
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index d0e8b99..0e91371 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -18,8 +18,8 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest.openstack.common import log as logging
-from tempest.test import attr
-from testtools.matchers import ContainsAll
+from tempest import test
+from testtools import matchers
LOG = logging.getLogger(__name__)
@@ -116,12 +116,12 @@
('details' if with_detail else '', key)
if key == 'metadata':
self.assertThat(volume[key].items(),
- ContainsAll(params[key].items()),
- msg)
+ matchers.ContainsAll(params[key]
+ .items()), msg)
else:
self.assertEqual(params[key], volume[key], msg)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_volume_list(self):
# Get a list of Volumes
# Fetch all volumes
@@ -130,7 +130,7 @@
self.assertVolumesIn(fetched_list, self.volume_list,
fields=VOLUME_FIELDS)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_list_with_details(self):
# Get a list of Volumes with details
# Fetch all Volumes
@@ -138,7 +138,7 @@
self.assertEqual(200, resp.status)
self.assertVolumesIn(fetched_list, self.volume_list)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_list_by_name(self):
volume = self.volume_list[data_utils.rand_int_id(0, 2)]
params = {'name': volume['name']}
@@ -147,7 +147,7 @@
self.assertEqual(1, len(fetched_vol), str(fetched_vol))
self.assertEqual(fetched_vol[0]['name'], volume['name'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_list_details_by_name(self):
volume = self.volume_list[data_utils.rand_int_id(0, 2)]
params = {'name': volume['name']}
@@ -156,43 +156,43 @@
self.assertEqual(1, len(fetched_vol), str(fetched_vol))
self.assertEqual(fetched_vol[0]['name'], volume['name'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volumes_list_by_status(self):
params = {'status': 'available'}
self._list_by_param_value_and_assert(params)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volumes_list_details_by_status(self):
params = {'status': 'available'}
self._list_by_param_value_and_assert(params, with_detail=True)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volumes_list_by_availability_zone(self):
volume = self.volume_list[data_utils.rand_int_id(0, 2)]
zone = volume['availability_zone']
params = {'availability_zone': zone}
self._list_by_param_value_and_assert(params)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volumes_list_details_by_availability_zone(self):
volume = self.volume_list[data_utils.rand_int_id(0, 2)]
zone = volume['availability_zone']
params = {'availability_zone': zone}
self._list_by_param_value_and_assert(params, with_detail=True)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_list_with_param_metadata(self):
# Test to list volumes when metadata param is given
params = {'metadata': self.metadata}
self._list_by_param_value_and_assert(params)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_list_with_detail_param_metadata(self):
# Test to list volumes details when metadata param is given
params = {'metadata': self.metadata}
self._list_by_param_value_and_assert(params, with_detail=True)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_list_param_display_name_and_status(self):
# Test to list volume when display name and status param is given
volume = self.volume_list[data_utils.rand_int_id(0, 2)]
@@ -200,7 +200,7 @@
'status': 'available'}
self._list_by_param_value_and_assert(params, expected_list=[volume])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_volume_list_with_detail_param_display_name_and_status(self):
# Test to list volume when name and status param is given
volume = self.volume_list[data_utils.rand_int_id(0, 2)]
diff --git a/tempest/auth.py b/tempest/auth.py
index 8cb3b2c..0e45161 100644
--- a/tempest/auth.py
+++ b/tempest/auth.py
@@ -14,11 +14,11 @@
# under the License.
import copy
+import datetime
import exceptions
import re
import urlparse
-from datetime import datetime
from tempest import config
from tempest.services.identity.json import identity_client as json_id
from tempest.services.identity.v3.json import identity_client as json_v3id
@@ -291,9 +291,9 @@
def is_expired(self, auth_data):
_, access = auth_data
- expiry = datetime.strptime(access['token']['expires'],
- self.EXPIRY_DATE_FORMAT)
- return expiry <= datetime.now()
+ expiry = datetime.datetime.strptime(access['token']['expires'],
+ self.EXPIRY_DATE_FORMAT)
+ return expiry <= datetime.datetime.now()
class KeystoneV3AuthProvider(KeystoneAuthProvider):
@@ -391,6 +391,6 @@
def is_expired(self, auth_data):
_, access = auth_data
- expiry = datetime.strptime(access['expires_at'],
- self.EXPIRY_DATE_FORMAT)
- return expiry <= datetime.now()
+ expiry = datetime.datetime.strptime(access['expires_at'],
+ self.EXPIRY_DATE_FORMAT)
+ return expiry <= datetime.datetime.now()
diff --git a/tempest/cli/simple_read_only/test_nova.py b/tempest/cli/simple_read_only/test_nova.py
index b0264d0..d0b6028 100644
--- a/tempest/cli/simple_read_only/test_nova.py
+++ b/tempest/cli/simple_read_only/test_nova.py
@@ -154,12 +154,18 @@
def test_admin_usage_list(self):
self.nova('usage-list')
+ @testtools.skipIf(not CONF.service_available.cinder,
+ "Skipped as Cinder is not available")
def test_admin_volume_list(self):
self.nova('volume-list')
+ @testtools.skipIf(not CONF.service_available.cinder,
+ "Skipped as Cinder is not available")
def test_admin_volume_snapshot_list(self):
self.nova('volume-snapshot-list')
+ @testtools.skipIf(not CONF.service_available.cinder,
+ "Skipped as Cinder is not available")
def test_admin_volume_type_list(self):
self.nova('volume-type-list')
diff --git a/tempest/clients.py b/tempest/clients.py
index 8db399a..e16d0f4 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -114,6 +114,8 @@
from tempest.services.compute.xml.volumes_extensions_client import \
VolumesExtensionsClientXML
from tempest.services.data_processing.v1_1.client import DataProcessingClient
+from tempest.services.database.json.flavors_client import \
+ DatabaseFlavorsClientJSON
from tempest.services.identity.json.identity_client import IdentityClientJSON
from tempest.services.identity.json.identity_client import TokenClientJSON
from tempest.services.identity.v3.json.credentials_client import \
@@ -330,6 +332,8 @@
self.volumes_extension_client = VolumeExtensionClientJSON(
self.auth_provider)
self.hosts_v3_client = HostsV3ClientJSON(self.auth_provider)
+ self.database_flavors_client = DatabaseFlavorsClientJSON(
+ self.auth_provider)
if CONF.service_available.ceilometer:
self.telemetry_client = TelemetryClientJSON(
self.auth_provider)
diff --git a/tempest/common/generate_json.py b/tempest/common/generate_json.py
deleted file mode 100644
index c8e86dc..0000000
--- a/tempest/common/generate_json.py
+++ /dev/null
@@ -1,265 +0,0 @@
-# Copyright 2014 Red Hat, Inc. & Deutsche Telekom AG
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import copy
-import jsonschema
-
-from tempest.openstack.common import log as logging
-
-LOG = logging.getLogger(__name__)
-
-
-def generate_valid(schema):
- """
- Create a valid dictionary based on the types in a json schema.
- """
- LOG.debug("generate_valid: %s" % schema)
- schema_type = schema["type"]
- if isinstance(schema_type, list):
- # Just choose the first one since all are valid.
- schema_type = schema_type[0]
- return type_map_valid[schema_type](schema)
-
-
-def generate_valid_string(schema):
- size = schema.get("minLength", 0)
- # TODO(dkr mko): handle format and pattern
- return "x" * size
-
-
-def generate_valid_integer(schema):
- # TODO(dkr mko): handle multipleOf
- if "minimum" in schema:
- minimum = schema["minimum"]
- if "exclusiveMinimum" not in schema:
- return minimum
- else:
- return minimum + 1
- if "maximum" in schema:
- maximum = schema["maximum"]
- if "exclusiveMaximum" not in schema:
- return maximum
- else:
- return maximum - 1
- return 0
-
-
-def generate_valid_object(schema):
- obj = {}
- for k, v in schema["properties"].iteritems():
- obj[k] = generate_valid(v)
- return obj
-
-
-def generate_invalid(schema):
- """
- Generate an invalid json dictionary based on a schema.
- Only one value is mis-generated for each dictionary created.
-
- 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)
- """
- LOG.debug("generate_invalid: %s" % 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 = []
- for generator in type_map_invalid[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"
- % generator)
- LOG.debug("result: %s" % result)
- return result
-
-
-def _check_for_expected_result(name, schema):
- expected_result = None
- if "results" in schema:
- if name in schema["results"]:
- expected_result = schema["results"][name]
- return expected_result
-
-
-def generator(fn):
- """
- Decorator for simple generators that simply return one value
- """
- def wrapped(schema):
- result = fn(schema)
- if result is not None:
- expected_result = _check_for_expected_result(fn.__name__, schema)
- return (fn.__name__, result, expected_result)
- return
- return wrapped
-
-
-@generator
-def gen_int(_):
- return 4
-
-
-@generator
-def gen_string(_):
- return "XXXXXX"
-
-
-def gen_none(schema):
- # Note(mkoderer): it's not using the decorator otherwise it'd be filtered
- expected_result = _check_for_expected_result('gen_none', schema)
- return ('gen_none', None, expected_result)
-
-
-@generator
-def gen_str_min_length(schema):
- min_length = schema.get("minLength", 0)
- if min_length > 0:
- return "x" * (min_length - 1)
-
-
-@generator
-def gen_str_max_length(schema):
- max_length = schema.get("maxLength", -1)
- if max_length > -1:
- return "x" * (max_length + 1)
-
-
-@generator
-def gen_int_min(schema):
- if "minimum" in schema:
- minimum = schema["minimum"]
- if "exclusiveMinimum" not in schema:
- minimum -= 1
- return minimum
-
-
-@generator
-def gen_int_max(schema):
- if "maximum" in schema:
- maximum = schema["maximum"]
- if "exclusiveMaximum" not in schema:
- maximum += 1
- return maximum
-
-
-def gen_obj_remove_attr(schema):
- invalids = []
- valid = generate_valid(schema)
- required = schema.get("required", [])
- for r in required:
- new_valid = copy.deepcopy(valid)
- del new_valid[r]
- invalids.append(("gen_obj_remove_attr", new_valid, None))
- return invalids
-
-
-@generator
-def gen_obj_add_attr(schema):
- valid = generate_valid(schema)
- if not schema.get("additionalProperties", True):
- new_valid = copy.deepcopy(valid)
- new_valid["$$$$$$$$$$"] = "xxx"
- return new_valid
-
-
-def gen_inv_prop_obj(schema):
- LOG.debug("generate_invalid_object: %s" % schema)
- valid = generate_valid(schema)
- invalids = []
- properties = schema["properties"]
-
- for k, v in properties.iteritems():
- for invalid in generate_invalid(v):
- LOG.debug(v)
- new_valid = copy.deepcopy(valid)
- 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
-
-
-type_map_valid = {
- "string": generate_valid_string,
- "integer": generate_valid_integer,
- "object": generate_valid_object
-}
-
-type_map_invalid = {
- "string": [
- gen_int,
- gen_none,
- gen_str_min_length,
- gen_str_max_length],
- "integer": [
- gen_string,
- gen_none,
- gen_int_min,
- gen_int_max],
- "object": [
- gen_obj_remove_attr,
- gen_obj_add_attr,
- gen_inv_prop_obj]
-}
-
-schema = {
- "type": "object",
- "properties": {
- "name": {"type": "string"},
- "http-method": {
- "enum": ["GET", "PUT", "HEAD",
- "POST", "PATCH", "DELETE", 'COPY']
- },
- "url": {"type": "string"},
- "json-schema": jsonschema._utils.load_schema("draft4"),
- "resources": {
- "type": "array",
- "items": {
- "oneOf": [
- {"type": "string"},
- {
- "type": "object",
- "properties": {
- "name": {"type": "string"},
- "expected_result": {"type": "integer"}
- }
- }
- ]
- }
- },
- "results": {
- "type": "object",
- "properties": {}
- }
- },
- "required": ["name", "http-method", "url"],
- "additionalProperties": False,
-}
-
-
-def validate_negative_test_schema(nts):
- jsonschema.validate(nts, schema)
diff --git a/tempest/common/generator/__init__.py b/tempest/common/generator/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/common/generator/__init__.py
diff --git a/tempest/common/generator/base_generator.py b/tempest/common/generator/base_generator.py
new file mode 100644
index 0000000..35f8158
--- /dev/null
+++ b/tempest/common/generator/base_generator.py
@@ -0,0 +1,141 @@
+# Copyright 2014 Deutsche Telekom AG
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import jsonschema
+
+from tempest.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+def _check_for_expected_result(name, schema):
+ expected_result = None
+ if "results" in schema:
+ if name in schema["results"]:
+ expected_result = schema["results"][name]
+ return expected_result
+
+
+def generator_type(*args):
+ def wrapper(func):
+ func.types = args
+ return func
+ return wrapper
+
+
+def simple_generator(fn):
+ """
+ Decorator for simple generators that return one value
+ """
+ def wrapped(self, schema):
+ result = fn(self, schema)
+ if result is not None:
+ expected_result = _check_for_expected_result(fn.__name__, schema)
+ return (fn.__name__, result, expected_result)
+ return
+ return wrapped
+
+
+class BasicGeneratorSet(object):
+ _instance = None
+
+ schema = {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "http-method": {
+ "enum": ["GET", "PUT", "HEAD",
+ "POST", "PATCH", "DELETE", 'COPY']
+ },
+ "url": {"type": "string"},
+ "json-schema": jsonschema._utils.load_schema("draft4"),
+ "resources": {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ {"type": "string"},
+ {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "expected_result": {"type": "integer"}
+ }
+ }
+ ]
+ }
+ },
+ "results": {
+ "type": "object",
+ "properties": {}
+ }
+ },
+ "required": ["name", "http-method", "url"],
+ "additionalProperties": False,
+ }
+
+ def __new__(cls, *args, **kwargs):
+ if not cls._instance:
+ cls._instance = super(BasicGeneratorSet, cls).__new__(cls, *args,
+ **kwargs)
+ return cls._instance
+
+ def __init__(self):
+ self.types_dict = {}
+ for m in dir(self):
+ if callable(getattr(self, m)) and not'__' in m:
+ method = getattr(self, m)
+ if hasattr(method, "types"):
+ for type in method.types:
+ if type not in self.types_dict:
+ self.types_dict[type] = []
+ self.types_dict[type].append(method)
+
+ def validate_schema(self, schema):
+ jsonschema.validate(schema, self.schema)
+
+ def generate(self, schema):
+ """
+ Generate an json dictionary based on a schema.
+ Only one value is mis-generated for each dictionary created.
+
+ 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)
+ """
+ LOG.debug("generate_invalid: %s" % 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 Exception("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
diff --git a/tempest/common/generator/negative_generator.py b/tempest/common/generator/negative_generator.py
new file mode 100644
index 0000000..4f3d2cd
--- /dev/null
+++ b/tempest/common/generator/negative_generator.py
@@ -0,0 +1,111 @@
+# Copyright 2014 Deutsche Telekom AG
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import copy
+
+import tempest.common.generator.base_generator as base
+import tempest.common.generator.valid_generator as valid
+from tempest.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+class NegativeTestGenerator(base.BasicGeneratorSet):
+ @base.generator_type("string")
+ @base.simple_generator
+ def gen_int(self, _):
+ return 4
+
+ @base.generator_type("integer")
+ @base.simple_generator
+ def gen_string(self, _):
+ return "XXXXXX"
+
+ @base.generator_type("integer", "string")
+ def gen_none(self, schema):
+ # Note(mkoderer): it's not using the decorator otherwise it'd be
+ # filtered
+ expected_result = base._check_for_expected_result('gen_none', schema)
+ return ('gen_none', None, expected_result)
+
+ @base.generator_type("string")
+ @base.simple_generator
+ def gen_str_min_length(self, schema):
+ min_length = schema.get("minLength", 0)
+ if min_length > 0:
+ return "x" * (min_length - 1)
+
+ @base.generator_type("string")
+ @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)
+
+ @base.generator_type("integer")
+ @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
+
+ @base.generator_type("integer")
+ @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
+
+ @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.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
diff --git a/tempest/common/generator/valid_generator.py b/tempest/common/generator/valid_generator.py
new file mode 100644
index 0000000..a99bbc0
--- /dev/null
+++ b/tempest/common/generator/valid_generator.py
@@ -0,0 +1,58 @@
+# Copyright 2014 Deutsche Telekom AG
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import tempest.common.generator.base_generator as base
+from tempest.openstack.common import log as logging
+
+
+LOG = logging.getLogger(__name__)
+
+
+class ValidTestGenerator(base.BasicGeneratorSet):
+ @base.generator_type("string")
+ @base.simple_generator
+ def generate_valid_string(self, schema):
+ size = schema.get("minLength", 0)
+ # TODO(dkr mko): handle format and pattern
+ return "x" * size
+
+ @base.generator_type("integer")
+ @base.simple_generator
+ def generate_valid_integer(self, schema):
+ # TODO(dkr mko): handle multipleOf
+ if "minimum" in schema:
+ minimum = schema["minimum"]
+ if "exclusiveMinimum" not in schema:
+ return minimum
+ else:
+ return minimum + 1
+ if "maximum" in schema:
+ maximum = schema["maximum"]
+ if "exclusiveMaximum" not in schema:
+ return maximum
+ else:
+ return maximum - 1
+ return 0
+
+ @base.generator_type("object")
+ @base.simple_generator
+ def generate_valid_object(self, schema):
+ obj = {}
+ for k, v in schema["properties"].iteritems():
+ obj[k] = self.generate_valid(v)
+ return obj
+
+ def generate_valid(self, schema):
+ return self.generate(schema)[0][1]
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 03dccd4..66b6fe7 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -25,7 +25,7 @@
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
-from tempest.services.compute.xml.common import xml_to_json
+from tempest.services.compute.xml import common
CONF = config.CONF
@@ -299,11 +299,11 @@
# Parse list-like xmls (users, roles, etc)
array = []
for child in element.getchildren():
- array.append(xml_to_json(child))
+ array.append(common.xml_to_json(child))
return array
# Parse one-item-like xmls (user, role, etc)
- return xml_to_json(element)
+ return common.xml_to_json(element)
def response_checker(self, method, url, headers, body, resp, resp_body):
if (resp.status in set((204, 205, 304)) or resp.status < 200 or
diff --git a/tempest/config.py b/tempest/config.py
index 0f5e23c..db81f6e 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -348,9 +348,6 @@
cfg.IntOpt('tenant_network_mask_bits',
default=28,
help="The mask bits for tenant ipv4 subnets"),
- cfg.BoolOpt('ipv6_enabled',
- default=True,
- help="Allow the execution of IPv6 tests"),
cfg.StrOpt('tenant_network_v6_cidr',
default="2003::/64",
help="The cidr block to allocate tenant ipv6 subnets from"),
@@ -375,6 +372,9 @@
title='Enabled network service features')
NetworkFeaturesGroup = [
+ cfg.BoolOpt('ipv6',
+ default=True,
+ help="Allow the execution of IPv6 tests"),
cfg.ListOpt('api_extensions',
default=['all'],
help='A list of enabled network extensions with a special '
@@ -493,6 +493,17 @@
"features are expected to be enabled"),
]
+database_group = cfg.OptGroup(name='database',
+ title='Database Service Options')
+
+DatabaseGroup = [
+ cfg.StrOpt('catalog_type',
+ default='database',
+ help="Catalog type of the Database service."),
+ cfg.StrOpt('db_flavor_ref',
+ default="1",
+ help="Valid primary flavor to use in database tests."),
+]
orchestration_group = cfg.OptGroup(name='orchestration',
title='Orchestration Service Options')
@@ -522,7 +533,7 @@
default=1,
help="Time in seconds between build status checks."),
cfg.IntOpt('build_timeout',
- default=300,
+ default=600,
help="Timeout in seconds to wait for a stack to build."),
cfg.StrOpt('instance_type',
default='m1.micro',
@@ -739,6 +750,9 @@
cfg.BoolOpt('ironic',
default=False,
help="Whether or not Ironic is expected to be available"),
+ cfg.BoolOpt('trove',
+ default=False,
+ help="Whether or not Trove is expected to be available"),
]
debug_group = cfg.OptGroup(name="debug",
@@ -806,6 +820,15 @@
help="Number of seconds to wait on a CLI timeout"),
]
+negative_group = cfg.OptGroup(name='negative', title="Negative Test Options")
+
+NegativeGroup = [
+ cfg.StrOpt('test_generator',
+ default='tempest.common.' +
+ 'generator.negative_generator.NegativeTestGenerator',
+ help="Test generator class for all negative tests"),
+]
+
def register_opts():
register_opt_group(cfg.CONF, compute_group, ComputeGroup)
@@ -825,6 +848,7 @@
register_opt_group(cfg.CONF, object_storage_group, ObjectStoreGroup)
register_opt_group(cfg.CONF, object_storage_feature_group,
ObjectStoreFeaturesGroup)
+ register_opt_group(cfg.CONF, database_group, DatabaseGroup)
register_opt_group(cfg.CONF, orchestration_group, OrchestrationGroup)
register_opt_group(cfg.CONF, telemetry_group, TelemetryGroup)
register_opt_group(cfg.CONF, dashboard_group, DashboardGroup)
@@ -840,6 +864,7 @@
register_opt_group(cfg.CONF, baremetal_group, BaremetalGroup)
register_opt_group(cfg.CONF, input_scenario_group, InputScenarioGroup)
register_opt_group(cfg.CONF, cli_group, CLIGroup)
+ register_opt_group(cfg.CONF, negative_group, NegativeGroup)
# this should never be called outside of this class
@@ -866,6 +891,7 @@
self.object_storage = cfg.CONF['object-storage']
self.object_storage_feature_enabled = cfg.CONF[
'object-storage-feature-enabled']
+ self.database = cfg.CONF.database
self.orchestration = cfg.CONF.orchestration
self.telemetry = cfg.CONF.telemetry
self.dashboard = cfg.CONF.dashboard
@@ -879,6 +905,7 @@
self.baremetal = cfg.CONF.baremetal
self.input_scenario = cfg.CONF['input-scenario']
self.cli = cfg.CONF.cli
+ self.negative = cfg.CONF.negative
if not self.compute_admin.username:
self.compute_admin.username = self.identity.admin_username
self.compute_admin.password = self.identity.admin_password
diff --git a/tempest/services/compute/v3/json/aggregates_client.py b/tempest/services/compute/v3/json/aggregates_client.py
index 20ce87b..fddf5df 100644
--- a/tempest/services/compute/v3/json/aggregates_client.py
+++ b/tempest/services/compute/v3/json/aggregates_client.py
@@ -15,14 +15,14 @@
import json
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
from tempest import exceptions
CONF = config.CONF
-class AggregatesV3ClientJSON(RestClient):
+class AggregatesV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(AggregatesV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/compute/v3/json/availability_zone_client.py b/tempest/services/compute/v3/json/availability_zone_client.py
index 4a6db55..bad2de9 100644
--- a/tempest/services/compute/v3/json/availability_zone_client.py
+++ b/tempest/services/compute/v3/json/availability_zone_client.py
@@ -15,13 +15,13 @@
import json
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
CONF = config.CONF
-class AvailabilityZoneV3ClientJSON(RestClient):
+class AvailabilityZoneV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(AvailabilityZoneV3ClientJSON, self).__init__(
diff --git a/tempest/services/compute/v3/json/certificates_client.py b/tempest/services/compute/v3/json/certificates_client.py
index 620eedf..f8beeb9 100644
--- a/tempest/services/compute/v3/json/certificates_client.py
+++ b/tempest/services/compute/v3/json/certificates_client.py
@@ -15,13 +15,13 @@
import json
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
CONF = config.CONF
-class CertificatesV3ClientJSON(RestClient):
+class CertificatesV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(CertificatesV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/compute/v3/json/extensions_client.py b/tempest/services/compute/v3/json/extensions_client.py
index 54f0aba..46f17a4 100644
--- a/tempest/services/compute/v3/json/extensions_client.py
+++ b/tempest/services/compute/v3/json/extensions_client.py
@@ -15,13 +15,13 @@
import json
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
CONF = config.CONF
-class ExtensionsV3ClientJSON(RestClient):
+class ExtensionsV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(ExtensionsV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/compute/v3/json/flavors_client.py b/tempest/services/compute/v3/json/flavors_client.py
index f9df2b8..656bd84 100644
--- a/tempest/services/compute/v3/json/flavors_client.py
+++ b/tempest/services/compute/v3/json/flavors_client.py
@@ -16,13 +16,13 @@
import json
import urllib
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
CONF = config.CONF
-class FlavorsV3ClientJSON(RestClient):
+class FlavorsV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(FlavorsV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/compute/v3/json/hosts_client.py b/tempest/services/compute/v3/json/hosts_client.py
index 76af626..e27c7c6 100644
--- a/tempest/services/compute/v3/json/hosts_client.py
+++ b/tempest/services/compute/v3/json/hosts_client.py
@@ -15,13 +15,13 @@
import json
import urllib
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
CONF = config.CONF
-class HostsV3ClientJSON(RestClient):
+class HostsV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(HostsV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/compute/v3/json/hypervisor_client.py b/tempest/services/compute/v3/json/hypervisor_client.py
index e07a1fb..30e391f 100644
--- a/tempest/services/compute/v3/json/hypervisor_client.py
+++ b/tempest/services/compute/v3/json/hypervisor_client.py
@@ -15,13 +15,13 @@
import json
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
CONF = config.CONF
-class HypervisorV3ClientJSON(RestClient):
+class HypervisorV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(HypervisorV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/compute/v3/json/interfaces_client.py b/tempest/services/compute/v3/json/interfaces_client.py
index f8b9d09..b45426c 100644
--- a/tempest/services/compute/v3/json/interfaces_client.py
+++ b/tempest/services/compute/v3/json/interfaces_client.py
@@ -16,14 +16,14 @@
import json
import time
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
from tempest import exceptions
CONF = config.CONF
-class InterfacesV3ClientJSON(RestClient):
+class InterfacesV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(InterfacesV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/compute/v3/json/keypairs_client.py b/tempest/services/compute/v3/json/keypairs_client.py
index 821b86f..9ca4885 100644
--- a/tempest/services/compute/v3/json/keypairs_client.py
+++ b/tempest/services/compute/v3/json/keypairs_client.py
@@ -15,13 +15,13 @@
import json
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
CONF = config.CONF
-class KeyPairsV3ClientJSON(RestClient):
+class KeyPairsV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(KeyPairsV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/compute/v3/json/quotas_client.py b/tempest/services/compute/v3/json/quotas_client.py
index aa8bfaf..32e31a3 100644
--- a/tempest/services/compute/v3/json/quotas_client.py
+++ b/tempest/services/compute/v3/json/quotas_client.py
@@ -15,13 +15,13 @@
import json
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
CONF = config.CONF
-class QuotasV3ClientJSON(RestClient):
+class QuotasV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(QuotasV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index 389e6a4..256a730 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -19,7 +19,7 @@
import time
import urllib
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest.common import waiters
from tempest import config
from tempest import exceptions
@@ -27,7 +27,7 @@
CONF = config.CONF
-class ServersV3ClientJSON(RestClient):
+class ServersV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(ServersV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/compute/v3/json/services_client.py b/tempest/services/compute/v3/json/services_client.py
index e20dfde..b4e65a0 100644
--- a/tempest/services/compute/v3/json/services_client.py
+++ b/tempest/services/compute/v3/json/services_client.py
@@ -17,13 +17,13 @@
import json
import urllib
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
CONF = config.CONF
-class ServicesV3ClientJSON(RestClient):
+class ServicesV3ClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
super(ServicesV3ClientJSON, self).__init__(auth_provider)
diff --git a/tempest/services/database/__init__.py b/tempest/services/database/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/database/__init__.py
diff --git a/tempest/services/database/json/__init__.py b/tempest/services/database/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/database/json/__init__.py
diff --git a/tempest/services/database/json/flavors_client.py b/tempest/services/database/json/flavors_client.py
new file mode 100644
index 0000000..1a8a4c1
--- /dev/null
+++ b/tempest/services/database/json/flavors_client.py
@@ -0,0 +1,39 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.common import rest_client
+from tempest import config
+import urllib
+
+CONF = config.CONF
+
+
+class DatabaseFlavorsClientJSON(rest_client.RestClient):
+
+ def __init__(self, auth_provider):
+ super(DatabaseFlavorsClientJSON, self).__init__(auth_provider)
+ self.service = CONF.database.catalog_type
+
+ def list_db_flavors(self, params=None):
+ url = 'flavors'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ return resp, self._parse_resp(body)
+
+ def get_db_flavor_details(self, db_flavor_id):
+ resp, body = self.get("flavors/%s" % str(db_flavor_id))
+ return resp, self._parse_resp(body)
diff --git a/tempest/services/volume/json/backups_client.py b/tempest/services/volume/json/backups_client.py
index baaf5a0..183d06b 100644
--- a/tempest/services/volume/json/backups_client.py
+++ b/tempest/services/volume/json/backups_client.py
@@ -69,6 +69,13 @@
body = json.loads(body)
return resp, body['backup']
+ def list_backups_with_detail(self):
+ """Information for all the tenant's backups."""
+ url = "backups/detail"
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['backups']
+
def wait_for_backup_status(self, backup_id, status):
"""Waits for a Backup to reach a given status."""
resp, body = self.get_backup(backup_id)
diff --git a/tempest/test.py b/tempest/test.py
index c6e3d6e..2125047 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -27,10 +27,11 @@
import testtools
from tempest import clients
-from tempest.common import generate_json
+import tempest.common.generator.valid_generator as valid
from tempest.common import isolated_creds
from tempest import config
from tempest import exceptions
+from tempest.openstack.common import importutils
from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
@@ -400,7 +401,8 @@
"""
description = NegativeAutoTest.load_schema(description_file)
LOG.debug(description)
- generate_json.validate_negative_test_schema(description)
+ generator = importutils.import_class(CONF.negative.test_generator)()
+ generator.validate_schema(description)
schema = description.get("json-schema", None)
resources = description.get("resources", [])
scenario_list = []
@@ -416,7 +418,7 @@
"expected_result": expected_result
}))
if schema is not None:
- for invalid in generate_json.generate_invalid(schema):
+ for invalid in generator.generate(schema):
scenario_list.append((invalid[0],
{"schema": invalid[1],
"expected_result": invalid[2]}))
@@ -459,11 +461,12 @@
# Note(mkoderer): The resources list already contains an invalid
# entry (see get_resource).
# We just send a valid json-schema with it
- valid = None
+ valid_schema = None
schema = description.get("json-schema", None)
if schema:
- valid = generate_json.generate_valid(schema)
- new_url, body = self._http_arguments(valid, url, method)
+ 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)
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index 41b0558..e941606 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -43,6 +43,10 @@
swift = True
horizon = True
+ class fake_negative(object):
+ test_generator = 'tempest.common.' \
+ 'generator.negative_generator.NegativeTestGenerator'
+
compute_feature_enabled = fake_compute_feature_enabled()
volume_feature_enabled = fake_default_feature_enabled()
network_feature_enabled = fake_default_feature_enabled()
@@ -52,3 +56,5 @@
compute = fake_compute()
identity = fake_identity()
+
+ negative = fake_negative()
diff --git a/tempest/tests/negative/test_generate_json.py b/tempest/tests/negative/test_generate_json.py
index a0aa088..e09fcdf 100644
--- a/tempest/tests/negative/test_generate_json.py
+++ b/tempest/tests/negative/test_generate_json.py
@@ -13,11 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import generate_json as gen
+from tempest.common.generator import negative_generator
import tempest.test
-class TestGenerateJson(tempest.test.BaseTestCase):
+class TestNegativeGenerator(tempest.test.BaseTestCase):
fake_input_str = {"type": "string",
"minLength": 2,
@@ -35,19 +35,23 @@
}
}
+ def setUp(self):
+ super(TestNegativeGenerator, self).setUp()
+ self.negative = negative_generator.NegativeTestGenerator()
+
def _validate_result(self, data):
self.assertTrue(isinstance(data, list))
for t in data:
self.assertTrue(isinstance(t, tuple))
def test_generate_invalid_string(self):
- result = gen.generate_invalid(self.fake_input_str)
+ result = self.negative.generate(self.fake_input_str)
self._validate_result(result)
def test_generate_invalid_integer(self):
- result = gen.generate_invalid(self.fake_input_int)
+ result = self.negative.generate(self.fake_input_int)
self._validate_result(result)
def test_generate_invalid_obj(self):
- result = gen.generate_invalid(self.fake_input_obj)
+ result = self.negative.generate(self.fake_input_obj)
self._validate_result(result)
diff --git a/tempest/tests/negative/test_negative_auto_test.py b/tempest/tests/negative/test_negative_auto_test.py
index 4c59383..27ddc95 100644
--- a/tempest/tests/negative/test_negative_auto_test.py
+++ b/tempest/tests/negative/test_negative_auto_test.py
@@ -16,9 +16,11 @@
import mock
import tempest.test as test
+from tempest.tests import base
+from tempest.tests import fake_config
-class TestNegativeAutoTest(test.BaseTestCase):
+class TestNegativeAutoTest(base.TestCase):
# Fake entries
_interface = 'json'
_service = 'compute'
@@ -34,6 +36,10 @@
"resources": ["flavor", "volume", "image"]
}
+ def setUp(self):
+ super(TestNegativeAutoTest, self).setUp()
+ self.stubs.Set(test, 'CONF', fake_config.FakeConfig)
+
def _check_prop_entries(self, result, entry):
entries = [a for a in result if entry in a[0]]
self.assertIsNotNone(entries)