Merge "Adds "list hosts" test case - Cinder"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 251336e..937bbd3 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -220,14 +220,6 @@
# admin credentials are known. (boolean value)
#allow_tenant_isolation=false
-# If allow_tenant_isolation is True and a tenant that would be
-# created for a given test already exists (such as from a
-# previously-failed run), re-use that tenant instead of
-# failing because of the conflict. Note that this would result
-# in the tenant being deleted at the end of a subsequent
-# successful run. (boolean value)
-#allow_tenant_reuse=true
-
# Valid secondary image reference to be used in tests. (string
# value)
#image_ref={$IMAGE_ID}
@@ -236,10 +228,10 @@
# value)
#image_ref_alt={$IMAGE_ID_ALT}
-# Valid primary flavor to use in tests. (integer value)
+# Valid primary flavor to use in tests. (string value)
#flavor_ref=1
-# Valid secondary flavor to be used in tests. (integer value)
+# Valid secondary flavor to be used in tests. (string value)
#flavor_ref_alt=2
# User name used to authenticate to an instance. (string
@@ -711,7 +703,7 @@
# Options defined in tempest.config
#
-# Set to True if the container quota middleware is enabled
+# Set to True if the Container Quota middleware is enabled
# (boolean value)
#container_quotas=true
@@ -723,6 +715,10 @@
# (boolean value)
#crossdomain=true
+# Set to True if the TempURL middleware is enabled (boolean
+# value)
+#tempurl=true
+
[volume-feature-enabled]
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 8d2fcac..c5a8772 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import uuid
+
from tempest.api import compute
from tempest.api.compute import base
from tempest.common.utils import data_utils
@@ -53,18 +55,16 @@
self.assertEqual(resp.status, 202)
self.client.wait_for_resource_deletion(flavor_id)
- @attr(type='gate')
- def test_create_flavor(self):
+ def _create_flavor(self, flavor_id):
# Create a flavor and ensure it is listed
# This operation requires the user to have 'admin' role
flavor_name = data_utils.rand_name(self.flavor_name_prefix)
- new_flavor_id = data_utils.rand_int_id(start=1000)
# Create the flavor
resp, flavor = self.client.create_flavor(flavor_name,
self.ram, self.vcpus,
self.disk,
- new_flavor_id,
+ flavor_id,
ephemeral=self.ephemeral,
swap=self.swap,
rxtx=self.rxtx)
@@ -74,7 +74,6 @@
self.assertEqual(flavor['vcpus'], self.vcpus)
self.assertEqual(flavor['disk'], self.disk)
self.assertEqual(flavor['ram'], self.ram)
- self.assertEqual(int(flavor['id']), new_flavor_id)
self.assertEqual(flavor['swap'], self.swap)
self.assertEqual(flavor['rxtx_factor'], self.rxtx)
self.assertEqual(flavor['OS-FLV-EXT-DATA:ephemeral'],
@@ -82,10 +81,32 @@
self.assertEqual(flavor['os-flavor-access:is_public'], True)
# Verify flavor is retrieved
- resp, flavor = self.client.get_flavor_details(new_flavor_id)
+ resp, flavor = self.client.get_flavor_details(flavor['id'])
self.assertEqual(resp.status, 200)
self.assertEqual(flavor['name'], flavor_name)
+ return flavor['id']
+
+ @attr(type='gate')
+ def test_create_flavor_with_int_id(self):
+ flavor_id = data_utils.rand_int_id(start=1000)
+ new_flavor_id = self._create_flavor(flavor_id)
+ self.assertEqual(new_flavor_id, str(flavor_id))
+
+ @attr(type='gate')
+ def test_create_flavor_with_uuid_id(self):
+ flavor_id = str(uuid.uuid4())
+ new_flavor_id = self._create_flavor(flavor_id)
+ self.assertEqual(new_flavor_id, flavor_id)
+
+ @attr(type='gate')
+ def test_create_flavor_with_none_id(self):
+ # If nova receives a request with None as flavor_id,
+ # nova generates flavor_id of uuid.
+ flavor_id = None
+ new_flavor_id = self._create_flavor(flavor_id)
+ self.assertEqual(new_flavor_id, str(uuid.UUID(new_flavor_id)))
+
@attr(type='gate')
def test_create_flavor_verify_entry_in_list_details(self):
# Create a flavor and ensure it's details are listed
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 7ef5466..e3edd7c 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -181,17 +181,18 @@
return resp, image
@classmethod
- def rebuild_server(cls, **kwargs):
+ def rebuild_server(cls, server_id, **kwargs):
# Destroy an existing server and creates a new one
- try:
- cls.servers_client.delete_server(cls.server_id)
- cls.servers_client.wait_for_server_termination(cls.server_id)
- except Exception as exc:
- LOG.exception(exc)
- pass
+ if server_id:
+ try:
+ cls.servers_client.delete_server(server_id)
+ cls.servers_client.wait_for_server_termination(server_id)
+ except Exception as exc:
+ LOG.exception(exc)
+ pass
resp, server = cls.create_test_server(wait_until='ACTIVE', **kwargs)
- cls.server_id = server['id']
cls.password = server['adminPass']
+ return server['id']
class BaseV2ComputeAdminTest(BaseV2ComputeTest):
@@ -258,17 +259,17 @@
return resp, image
@classmethod
- def rebuild_server(cls, **kwargs):
+ def rebuild_server(cls, server_id, **kwargs):
# Destroy an existing server and creates a new one
try:
- cls.servers_client.delete_server(cls.server_id)
- cls.servers_client.wait_for_server_termination(cls.server_id)
+ cls.servers_client.delete_server(server_id)
+ cls.servers_client.wait_for_server_termination(server_id)
except Exception as exc:
LOG.exception(exc)
pass
resp, server = cls.create_test_server(wait_until='ACTIVE', **kwargs)
- cls.server_id = server['id']
cls.password = server['admin_password']
+ return server['id']
class BaseV3ComputeAdminTest(BaseV3ComputeTest):
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index eac98ea..fa99422 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -48,7 +48,7 @@
def test_get_flavor(self):
# The expected flavor details should be returned
resp, flavor = self.client.get_flavor_details(self.flavor_ref)
- self.assertEqual(self.flavor_ref, int(flavor['id']))
+ self.assertEqual(self.flavor_ref, flavor['id'])
@attr(type=['negative', 'gate'])
def test_get_non_existant_flavor(self):
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index a06309a..f4ad449 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -15,10 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
from tempest.test import attr
@@ -32,7 +30,7 @@
def setUpClass(cls):
super(FloatingIPsTestJSON, cls).setUpClass()
cls.client = cls.floating_ips_client
- cls.servers_client = cls.servers_client
+ #cls.servers_client = cls.servers_client
# Server creation
resp, server = cls.create_test_server(wait_until='ACTIVE')
@@ -41,17 +39,6 @@
resp, body = cls.client.create_floating_ip()
cls.floating_ip_id = body['id']
cls.floating_ip = body['ip']
- # Generating a nonexistent floatingIP id
- cls.floating_ip_ids = []
- resp, body = cls.client.list_floating_ips()
- for i in range(len(body)):
- cls.floating_ip_ids.append(body[i]['id'])
- while True:
- cls.non_exist_id = data_utils.rand_int_id(start=999)
- if cls.config.service_available.neutron:
- cls.non_exist_id = str(uuid.uuid4())
- if cls.non_exist_id not in cls.floating_ip_ids:
- break
@classmethod
def tearDownClass(cls):
@@ -76,14 +63,6 @@
# Deleting the floating IP which is created in this method
self.client.delete_floating_ip(floating_ip_id_allocated)
- @attr(type=['negative', 'gate'])
- def test_allocate_floating_ip_from_nonexistent_pool(self):
- # Positive test:Allocation of a new floating IP from a nonexistent_pool
- # to a project should fail
- self.assertRaises(exceptions.NotFound,
- self.client.create_floating_ip,
- "non_exist_pool")
-
@attr(type='gate')
def test_delete_floating_ip(self):
# Positive test:Deletion of valid floating IP from project
@@ -115,38 +94,13 @@
self.server_id)
self.assertEqual(202, resp.status)
- @attr(type=['negative', 'gate'])
- def test_delete_nonexistant_floating_ip(self):
- # Negative test:Deletion of a nonexistent floating IP
- # from project should fail
-
- # Deleting the non existent floating IP
- self.assertRaises(exceptions.NotFound, self.client.delete_floating_ip,
- self.non_exist_id)
-
- @attr(type=['negative', 'gate'])
- def test_associate_nonexistant_floating_ip(self):
- # Negative test:Association of a non existent floating IP
- # to specific server should fail
- # Associating non existent floating IP
- self.assertRaises(exceptions.NotFound,
- self.client.associate_floating_ip_to_server,
- "0.0.0.0", self.server_id)
-
- @attr(type=['negative', 'gate'])
- def test_dissociate_nonexistant_floating_ip(self):
- # Negative test:Dissociation of a non existent floating IP should fail
- # Dissociating non existent floating IP
- self.assertRaises(exceptions.NotFound,
- self.client.disassociate_floating_ip_from_server,
- "0.0.0.0", self.server_id)
-
@attr(type='gate')
def test_associate_already_associated_floating_ip(self):
# positive test:Association of an already associated floating IP
# to specific server should change the association of the Floating IP
# Create server so as to use for Multiple association
- resp, body = self.servers_client.create_server('floating-server2',
+ new_name = rand_name('floating_server')
+ resp, body = self.servers_client.create_server(new_name,
self.image_ref,
self.flavor_ref)
self.servers_client.wait_for_server_status(body['id'], 'ACTIVE')
@@ -173,14 +127,6 @@
self.client.disassociate_floating_ip_from_server,
self.floating_ip, self.server_id)
- @attr(type=['negative', 'gate'])
- def test_associate_ip_to_server_without_passing_floating_ip(self):
- # Negative test:Association of empty floating IP to specific server
- # should raise NotFound exception
- self.assertRaises(exceptions.NotFound,
- self.client.associate_floating_ip_to_server,
- '', self.server_id)
-
class FloatingIPsTestXML(FloatingIPsTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
new file mode 100644
index 0000000..89315bb
--- /dev/null
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -0,0 +1,94 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import uuid
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class FloatingIPsNegativeTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+ server_id = None
+
+ @classmethod
+ def setUpClass(cls):
+ super(FloatingIPsNegativeTestJSON, cls).setUpClass()
+ cls.client = cls.floating_ips_client
+
+ # Server creation
+ resp, server = cls.create_test_server(wait_until='ACTIVE')
+ cls.server_id = server['id']
+ # Generating a nonexistent floatingIP id
+ cls.floating_ip_ids = []
+ resp, body = cls.client.list_floating_ips()
+ for i in range(len(body)):
+ cls.floating_ip_ids.append(body[i]['id'])
+ while True:
+ cls.non_exist_id = data_utils.rand_int_id(start=999)
+ if cls.config.service_available.neutron:
+ cls.non_exist_id = str(uuid.uuid4())
+ if cls.non_exist_id not in cls.floating_ip_ids:
+ break
+
+ @attr(type=['negative', 'gate'])
+ def test_allocate_floating_ip_from_nonexistent_pool(self):
+ # Negative test:Allocation of a new floating IP from a nonexistent_pool
+ # to a project should fail
+ self.assertRaises(exceptions.NotFound,
+ self.client.create_floating_ip,
+ "non_exist_pool")
+
+ @attr(type=['negative', 'gate'])
+ def test_delete_nonexistent_floating_ip(self):
+ # Negative test:Deletion of a nonexistent floating IP
+ # from project should fail
+
+ # Deleting the non existent floating IP
+ self.assertRaises(exceptions.NotFound, self.client.delete_floating_ip,
+ self.non_exist_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_associate_nonexistent_floating_ip(self):
+ # Negative test:Association of a non existent floating IP
+ # to specific server should fail
+ # Associating non existent floating IP
+ self.assertRaises(exceptions.NotFound,
+ self.client.associate_floating_ip_to_server,
+ "0.0.0.0", self.server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_dissociate_nonexistent_floating_ip(self):
+ # Negative test:Dissociation of a non existent floating IP should fail
+ # Dissociating non existent floating IP
+ self.assertRaises(exceptions.NotFound,
+ self.client.disassociate_floating_ip_from_server,
+ "0.0.0.0", self.server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_associate_ip_to_server_without_passing_floating_ip(self):
+ # Negative test:Association of empty floating IP to specific server
+ # should raise NotFound exception
+ self.assertRaises(exceptions.NotFound,
+ self.client.associate_floating_ip_to_server,
+ '', self.server_id)
+
+
+class FloatingIPsNegativeTestXML(FloatingIPsNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 612c110..18e32d8 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -49,7 +49,7 @@
LOG.exception(exc)
# Rebuild server if cannot reach the ACTIVE state
# Usually it means the server had a serius accident
- self.rebuild_server()
+ self.server_id = self.rebuild_server(self.server_id)
@classmethod
def setUpClass(cls):
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index b4e778c..4cd41ee 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -50,7 +50,7 @@
LOG.exception(exc)
# Rebuild server if cannot reach the ACTIVE state
# Usually it means the server had a serius accident
- self.rebuild_server()
+ self.server_id = self.rebuild_server(self.server_id)
@classmethod
def setUpClass(cls):
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 44ce405..24ade96 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -72,7 +72,7 @@
str(netaddr.IPAddress(self.accessIPv6)))
self.assertEqual(self.name, self.server['name'])
self.assertEqual(self.image_ref, self.server['image']['id'])
- self.assertEqual(str(self.flavor_ref), self.server['flavor']['id'])
+ self.assertEqual(self.flavor_ref, self.server['flavor']['id'])
self.assertEqual(self.meta, self.server['metadata'])
@attr(type='smoke')
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 5abde56..5fa4c35 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -45,13 +45,13 @@
self.client.wait_for_server_status(self.server_id, 'ACTIVE')
except Exception:
# Rebuild server if something happened to it during a test
- self.rebuild_server()
+ self.server_id = self.rebuild_server(self.server_id)
@classmethod
def setUpClass(cls):
super(ServerActionsTestJSON, cls).setUpClass()
cls.client = cls.servers_client
- cls.rebuild_server()
+ cls.server_id = cls.rebuild_server(None)
@testtools.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
'Change password not available.')
@@ -128,7 +128,7 @@
self.assertEqual(self.server_id, rebuilt_server['id'])
rebuilt_image_id = rebuilt_server['image']['id']
self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
- self.assertEqual(self.flavor_ref, int(rebuilt_server['flavor']['id']))
+ self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
# Verify the server properties after the rebuild completes
self.client.wait_for_server_status(rebuilt_server['id'], 'ACTIVE')
@@ -147,8 +147,8 @@
resp, server = self.client.get_server(self.server_id)
current_flavor = server['flavor']['id']
new_flavor_ref = self.flavor_ref_alt \
- if int(current_flavor) == self.flavor_ref else self.flavor_ref
- return int(current_flavor), int(new_flavor_ref)
+ if current_flavor == self.flavor_ref else self.flavor_ref
+ return current_flavor, new_flavor_ref
@testtools.skipIf(not resize_available, 'Resize not available.')
@attr(type='smoke')
@@ -167,7 +167,7 @@
self.client.wait_for_server_status(self.server_id, 'ACTIVE')
resp, server = self.client.get_server(self.server_id)
- self.assertEqual(new_flavor_ref, int(server['flavor']['id']))
+ self.assertEqual(new_flavor_ref, server['flavor']['id'])
@testtools.skipIf(not resize_available, 'Resize not available.')
@attr(type='gate')
@@ -189,7 +189,7 @@
resp, server = self.client.get_server(self.server_id)
start = int(time.time())
- while int(server['flavor']['id']) != previous_flavor_ref:
+ while server['flavor']['id'] != previous_flavor_ref:
time.sleep(self.build_interval)
resp, server = self.client.get_server(self.server_id)
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 5ec0cbe..7b86d2d 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -34,7 +34,7 @@
try:
self.client.wait_for_server_status(self.server_id, 'ACTIVE')
except Exception:
- self.rebuild_server()
+ self.server_id = self.rebuild_server(self.server_id)
@classmethod
def setUpClass(cls):
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 92b9e30..dc78a47 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -45,13 +45,13 @@
self.client.wait_for_server_status(self.server_id, 'ACTIVE')
except Exception:
# Rebuild server if something happened to it during a test
- self.rebuild_server()
+ self.server_id = self.rebuild_server(self.server_id)
@classmethod
def setUpClass(cls):
super(ServerActionsV3TestJSON, cls).setUpClass()
cls.client = cls.servers_client
- cls.rebuild_server()
+ cls.server_id = cls.rebuild_server(None)
@testtools.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
'Change password not available.')
@@ -128,7 +128,7 @@
self.assertEqual(self.server_id, rebuilt_server['id'])
rebuilt_image_id = rebuilt_server['image']['id']
self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
- self.assertEqual(self.flavor_ref, int(rebuilt_server['flavor']['id']))
+ self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
# Verify the server properties after the rebuild completes
self.client.wait_for_server_status(rebuilt_server['id'], 'ACTIVE')
@@ -147,8 +147,8 @@
resp, server = self.client.get_server(self.server_id)
current_flavor = server['flavor']['id']
new_flavor_ref = self.flavor_ref_alt \
- if int(current_flavor) == self.flavor_ref else self.flavor_ref
- return int(current_flavor), int(new_flavor_ref)
+ if current_flavor == self.flavor_ref else self.flavor_ref
+ return current_flavor, new_flavor_ref
@testtools.skipIf(not resize_available, 'Resize not available.')
@attr(type='smoke')
@@ -167,7 +167,7 @@
self.client.wait_for_server_status(self.server_id, 'ACTIVE')
resp, server = self.client.get_server(self.server_id)
- self.assertEqual(new_flavor_ref, int(server['flavor']['id']))
+ self.assertEqual(new_flavor_ref, server['flavor']['id'])
@testtools.skipIf(not resize_available, 'Resize not available.')
@attr(type='gate')
@@ -189,7 +189,7 @@
resp, server = self.client.get_server(self.server_id)
start = int(time.time())
- while int(server['flavor']['id']) != previous_flavor_ref:
+ while server['flavor']['id'] != previous_flavor_ref:
time.sleep(self.build_interval)
resp, server = self.client.get_server(self.server_id)
diff --git a/tempest/api/compute/v3/servers/test_servers_negative.py b/tempest/api/compute/v3/servers/test_servers_negative.py
index 5ec0cbe..7b86d2d 100644
--- a/tempest/api/compute/v3/servers/test_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_servers_negative.py
@@ -34,7 +34,7 @@
try:
self.client.wait_for_server_status(self.server_id, 'ACTIVE')
except Exception:
- self.rebuild_server()
+ self.server_id = self.rebuild_server(self.server_id)
@classmethod
def setUpClass(cls):
diff --git a/tempest/api/network/common.py b/tempest/api/network/common.py
index 43e7f68..ab19fa8 100644
--- a/tempest/api/network/common.py
+++ b/tempest/api/network/common.py
@@ -94,3 +94,18 @@
def delete(self):
self.client.delete_port(self.id)
+
+
+class DeletableSecurityGroup(DeletableResource):
+
+ def delete(self):
+ self.client.delete_security_group(self.id)
+
+
+class DeletableSecurityGroupRule(DeletableResource):
+
+ def __repr__(self):
+ return '<%s id="%s">' % (self.__class__.__name__, self.id)
+
+ def delete(self):
+ self.client.delete_security_group_rule(self.id)
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index 9e405d6..1eea30a 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -16,7 +16,7 @@
from tempest.api.object_storage import base
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class StaticWebTest(base.BaseObjectTest):
@@ -48,7 +48,7 @@
cls.data.teardown_all()
super(StaticWebTest, cls).tearDownClass()
- @attr('gate')
+ @test.attr('gate')
def test_web_index(self):
headers = {'web-index': self.object_name}
@@ -59,7 +59,7 @@
# we should retrieve the self.object_name file
resp, body = self.custom_account_client.request("GET",
self.container_name)
- self.assertEqual(resp['status'], '200')
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertEqual(body, self.object_data)
# clean up before exiting
@@ -70,7 +70,7 @@
self.container_name)
self.assertNotIn('x-container-meta-web-index', body)
- @attr('gate')
+ @test.attr('gate')
def test_web_listing(self):
headers = {'web-listings': 'true'}
@@ -81,7 +81,7 @@
# we should retrieve a listing of objects
resp, body = self.custom_account_client.request("GET",
self.container_name)
- self.assertEqual(resp['status'], '200')
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertIn(self.object_name, body)
# clean up before exiting
@@ -91,3 +91,41 @@
_, body = self.container_client.list_container_metadata(
self.container_name)
self.assertNotIn('x-container-meta-web-listings', body)
+
+ @test.attr('gate')
+ def test_web_listing_css(self):
+ headers = {'web-listings': 'true',
+ 'web-listings-css': 'listings.css'}
+
+ self.container_client.update_container_metadata(
+ self.container_name, metadata=headers)
+
+ # test GET on http://account_url/container_name
+ # we should retrieve a listing of objects
+ resp, body = self.custom_account_client.request("GET",
+ self.container_name)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertIn(self.object_name, body)
+ css = '<link rel="stylesheet" type="text/css" href="listings.css" />'
+ self.assertIn(css, body)
+
+ @test.attr('gate')
+ def test_web_error(self):
+ headers = {'web-listings': 'true',
+ 'web-error': self.object_name}
+
+ self.container_client.update_container_metadata(
+ self.container_name, metadata=headers)
+
+ # Create object to return when requested object not found
+ object_name_404 = "404" + self.object_name
+ object_data_404 = data_utils.arbitrary_string()
+ self.object_client.create_object(self.container_name,
+ object_name_404,
+ object_data_404)
+
+ # Request non-existing object
+ resp, body = self.custom_object_client.get_object(self.container_name,
+ "notexisting")
+ self.assertEqual(resp['status'], '404')
+ self.assertEqual(body, object_data_404)
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
new file mode 100644
index 0000000..63393e4
--- /dev/null
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -0,0 +1,135 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+#
+# Author: Christian Schwede <christian.schwede@enovance.com>
+#
+# 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 hashlib
+import hmac
+import time
+import urlparse
+
+from tempest.api.object_storage import base
+from tempest.common.utils import data_utils
+from tempest.test import attr
+from tempest.test import HTTP_SUCCESS
+
+
+class ObjectFormPostTest(base.BaseObjectTest):
+
+ @classmethod
+ def setUpClass(cls):
+ super(ObjectFormPostTest, cls).setUpClass()
+ cls.container_name = data_utils.rand_name(name='TestContainer')
+ cls.object_name = data_utils.rand_name(name='ObjectTemp')
+
+ cls.container_client.create_container(cls.container_name)
+ cls.containers = [cls.container_name]
+
+ cls.key = 'Meta'
+ cls.metadata = {'Temp-URL-Key': cls.key}
+ cls.account_client.create_account_metadata(metadata=cls.metadata)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.account_client.delete_account_metadata(metadata=cls.metadata)
+ cls.delete_containers(cls.containers)
+ cls.data.teardown_all()
+ super(ObjectFormPostTest, cls).tearDownClass()
+
+ def get_multipart_form(self, expires=600):
+ path = "%s/%s/%s" % (
+ urlparse.urlparse(self.container_client.base_url).path,
+ self.container_name,
+ self.object_name)
+
+ redirect = ''
+ max_file_size = 104857600
+ max_file_count = 10
+ expires += int(time.time())
+ hmac_body = '%s\n%s\n%s\n%s\n%s' % (path,
+ redirect,
+ max_file_size,
+ max_file_count,
+ expires)
+
+ signature = hmac.new(self.key, hmac_body, hashlib.sha1).hexdigest()
+
+ fields = {'redirect': redirect,
+ 'max_file_size': str(max_file_size),
+ 'max_file_count': str(max_file_count),
+ 'expires': str(expires),
+ 'signature': signature}
+
+ boundary = '--boundary--'
+ data = []
+ for (key, value) in fields.items():
+ data.append('--' + boundary)
+ data.append('Content-Disposition: form-data; name="%s"' % key)
+ data.append('')
+ data.append(value)
+
+ data.append('--' + boundary)
+ data.append('Content-Disposition: form-data; '
+ 'name="file1"; filename="testfile"')
+ data.append('Content-Type: application/octet-stream')
+ data.append('')
+ data.append('hello world')
+
+ data.append('--' + boundary + '--')
+ data.append('')
+
+ body = '\r\n'.join(data)
+ content_type = 'multipart/form-data; boundary=%s' % boundary
+ return body, content_type
+
+ @attr(type='gate')
+ def test_post_object_using_form(self):
+ body, content_type = self.get_multipart_form()
+
+ headers = {'Content-Type': content_type,
+ 'Content-Length': str(len(body))}
+
+ url = "%s/%s/%s" % (self.container_client.base_url,
+ self.container_name,
+ self.object_name)
+
+ # Use a raw request, otherwise authentication headers are used
+ resp, body = self.object_client.http_obj.request(url, "POST",
+ body, headers=headers)
+ self.assertIn(int(resp['status']), HTTP_SUCCESS)
+
+ # Ensure object is available
+ resp, body = self.object_client.get("%s/%s%s" % (
+ self.container_name, self.object_name, "testfile"))
+ self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertEqual(body, "hello world")
+
+ @attr(type=['gate', 'negative'])
+ def test_post_object_using_form_expired(self):
+ body, content_type = self.get_multipart_form(expires=1)
+ time.sleep(2)
+
+ headers = {'Content-Type': content_type,
+ 'Content-Length': str(len(body))}
+
+ url = "%s/%s/%s" % (self.container_client.base_url,
+ self.container_name,
+ self.object_name)
+
+ # Use a raw request, otherwise authentication headers are used
+ resp, body = self.object_client.http_obj.request(url, "POST",
+ body, headers=headers)
+ self.assertEqual(int(resp['status']), 401)
+ self.assertIn('FormPost: Form Expired', body)
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 77f3a53..c8ce57a 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -22,6 +22,7 @@
from tempest.api.object_storage import base
from tempest.common.utils import data_utils
+from tempest import config
from tempest import exceptions
from tempest.test import attr
from tempest.test import HTTP_SUCCESS
@@ -29,9 +30,19 @@
class ObjectTempUrlTest(base.BaseObjectTest):
+ tempurl_available = \
+ config.TempestConfig().object_storage_feature_enabled.tempurl
+
@classmethod
def setUpClass(cls):
super(ObjectTempUrlTest, cls).setUpClass()
+
+ # skip this test if CORS isn't enabled in the conf file.
+ if not cls.tempurl_available:
+ skip_msg = ("%s skipped as TempUrl middleware not available"
+ % cls.__name__)
+ raise cls.skipException(skip_msg)
+
cls.container_name = data_utils.rand_name(name='TestContainer')
cls.container_client.create_container(cls.container_name)
cls.containers = [cls.container_name]
@@ -47,9 +58,9 @@
def tearDownClass(cls):
resp, _ = cls.account_client.delete_account_metadata(
metadata=cls.metadata)
- resp, _ = cls.account_client.list_account_metadata()
cls.delete_containers(cls.containers)
+
# delete the user setup created
cls.data.teardown_all()
super(ObjectTempUrlTest, cls).tearDownClass()
@@ -71,8 +82,11 @@
self.object_client.create_object(self.container_name,
self.object_name, self.data)
- def get_temp_url(self, container, object_name, method, expires,
- key):
+ def _get_expiry_date(self, expiration_time=1000):
+ return int(time.time() + expiration_time)
+
+ def _get_temp_url(self, container, object_name, method, expires,
+ key):
"""Create the temporary URL."""
path = "%s/%s/%s" % (
@@ -90,17 +104,17 @@
@attr(type='gate')
def test_get_object_using_temp_url(self):
- EXPIRATION_TIME = 10000 # high to ensure the test finishes.
- expires = int(time.time() + EXPIRATION_TIME)
+ expires = self._get_expiry_date()
# get a temp URL for the created object
- url = self.get_temp_url(self.container_name,
- self.object_name, "GET",
- expires, self.key)
+ url = self._get_temp_url(self.container_name,
+ self.object_name, "GET",
+ expires, self.key)
# trying to get object using temp url within expiry time
- _, body = self.object_client.get_object_using_temp_url(url)
+ resp, body = self.object_client.get_object_using_temp_url(url)
+ self.assertIn(int(resp['status']), HTTP_SUCCESS)
self.assertEqual(body, self.data)
# Testing a HEAD on this Temp URL
@@ -108,18 +122,35 @@
self.assertIn(int(resp['status']), HTTP_SUCCESS)
@attr(type='gate')
+ def test_get_object_using_temp_url_key_2(self):
+ key2 = 'Meta2-'
+ metadata = {'Temp-URL-Key-2': key2}
+ self.account_client.create_account_metadata(metadata=metadata)
+
+ self.account_client_metadata, _ = \
+ self.account_client.list_account_metadata()
+ self.assertIn('x-account-meta-temp-url-key-2',
+ self.account_client_metadata)
+
+ expires = self._get_expiry_date()
+ url = self._get_temp_url(self.container_name,
+ self.object_name, "GET",
+ expires, key2)
+ resp, body = self.object_client.get_object_using_temp_url(url)
+ self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertEqual(body, self.data)
+
+ @attr(type='gate')
def test_put_object_using_temp_url(self):
# make sure the metadata has been set
new_data = data_utils.arbitrary_string(
size=len(self.object_name),
base_text=data_utils.rand_name(name="random"))
- EXPIRATION_TIME = 10000 # high to ensure the test finishes.
- expires = int(time.time() + EXPIRATION_TIME)
-
- url = self.get_temp_url(self.container_name,
- self.object_name, "PUT",
- expires, self.key)
+ expires = self._get_expiry_date()
+ url = self._get_temp_url(self.container_name,
+ self.object_name, "PUT",
+ expires, self.key)
# trying to put random data in the object using temp url
resp, body = self.object_client.put_object_using_temp_url(
@@ -132,25 +163,24 @@
self.assertIn(int(resp['status']), HTTP_SUCCESS)
# Validate that the content of the object has been modified
- url = self.get_temp_url(self.container_name,
- self.object_name, "GET",
- expires, self.key)
+ url = self._get_temp_url(self.container_name,
+ self.object_name, "GET",
+ expires, self.key)
_, body = self.object_client.get_object_using_temp_url(url)
self.assertEqual(body, new_data)
@attr(type=['gate', 'negative'])
def test_get_object_after_expiration_time(self):
- EXPIRATION_TIME = 1
- expires = int(time.time() + EXPIRATION_TIME)
+ expires = self._get_expiry_date(1)
# get a temp URL for the created object
- url = self.get_temp_url(self.container_name,
- self.object_name, "GET",
- expires, self.key)
+ url = self._get_temp_url(self.container_name,
+ self.object_name, "GET",
+ expires, self.key)
- # temp URL is valid for 1 seconds, let's wait 3
- time.sleep(EXPIRATION_TIME + 2)
+ # temp URL is valid for 1 seconds, let's wait 2
+ time.sleep(2)
self.assertRaises(exceptions.Unauthorized,
self.object_client.get_object_using_temp_url,
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 4063eef..cb9ff11 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -47,7 +47,7 @@
super(VolumesActionsTest, cls).tearDownClass()
def _reset_volume_status(self, volume_id, status):
- #Reset the volume status
+ # Reset the volume status
resp, body = self.admin_volume_client.reset_volume_status(volume_id,
status)
return resp, body
@@ -57,6 +57,26 @@
self._reset_volume_status(self.volume['id'], 'available')
super(VolumesActionsTest, self).tearDown()
+ def _create_temp_volume(self):
+ # Create a temp volume for force delete tests
+ vol_name = utils.rand_name('Volume')
+ resp, temp_volume = self.client.create_volume(size=1,
+ display_name=vol_name)
+ self.client.wait_for_volume_status(temp_volume['id'], 'available')
+
+ return temp_volume
+
+ def _create_reset_and_force_delete_temp_volume(self, status=None):
+ # Create volume, reset volume status, and force delete temp volume
+ temp_volume = self._create_temp_volume()
+ if status:
+ resp, body = self._reset_volume_status(temp_volume['id'], status)
+ self.assertEqual(202, resp.status)
+ resp_delete, volume_delete = self.admin_volume_client.\
+ force_delete_volume(temp_volume['id'])
+ self.assertEqual(202, resp_delete.status)
+ self.client.wait_for_resource_deletion(temp_volume['id'])
+
@attr(type='gate')
def test_volume_reset_status(self):
# test volume reset status : available->error->available
@@ -84,6 +104,19 @@
resp_get, volume_get = self.client.get_volume(self.volume['id'])
self.assertEqual('in-use', volume_get['status'])
+ def test_volume_force_delete_when_volume_is_creating(self):
+ # test force delete when status of volume is creating
+ self._create_reset_and_force_delete_temp_volume('creating')
+
+ def test_volume_force_delete_when_volume_is_attaching(self):
+ # test force delete when status of volume is attaching
+ self._create_reset_and_force_delete_temp_volume('attaching')
+
+ @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')
+
class VolumesActionsTestXML(VolumesActionsTest):
_interface = "xml"
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index 742a354..c397b7c 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -62,7 +62,7 @@
password=self.password,
look_for_keys=self.look_for_keys,
key_filename=self.key_filename,
- timeout=self.timeout, pkey=self.pkey)
+ timeout=self.channel_timeout, pkey=self.pkey)
_timeout = False
break
except (socket.error,
diff --git a/tempest/config.py b/tempest/config.py
index 6af7d51..220fd04 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -103,25 +103,17 @@
"users. This option enables isolated test cases and "
"better parallel execution, but also requires that "
"OpenStack Identity API admin credentials are known."),
- cfg.BoolOpt('allow_tenant_reuse',
- default=True,
- help="If allow_tenant_isolation is True and a tenant that "
- "would be created for a given test already exists (such "
- "as from a previously-failed run), re-use that tenant "
- "instead of failing because of the conflict. Note that "
- "this would result in the tenant being deleted at the "
- "end of a subsequent successful run."),
cfg.StrOpt('image_ref',
default="{$IMAGE_ID}",
help="Valid secondary image reference to be used in tests."),
cfg.StrOpt('image_ref_alt',
default="{$IMAGE_ID_ALT}",
help="Valid secondary image reference to be used in tests."),
- cfg.IntOpt('flavor_ref',
- default=1,
+ cfg.StrOpt('flavor_ref',
+ default="1",
help="Valid primary flavor to use in tests."),
- cfg.IntOpt('flavor_ref_alt',
- default=2,
+ cfg.StrOpt('flavor_ref_alt',
+ default="2",
help='Valid secondary flavor to be used in tests.'),
cfg.StrOpt('image_ssh_user',
default="root",
@@ -406,7 +398,7 @@
ObjectStoreFeaturesGroup = [
cfg.BoolOpt('container_quotas',
default=True,
- help="Set to True if the container quota middleware "
+ help="Set to True if the Container Quota middleware "
"is enabled"),
cfg.BoolOpt('accounts_quotas',
default=True,
@@ -414,6 +406,9 @@
cfg.BoolOpt('crossdomain',
default=True,
help="Set to True if the Crossdomain middleware is enabled"),
+ cfg.BoolOpt('tempurl',
+ default=True,
+ help="Set to True if the TempURL middleware is enabled"),
]
diff --git a/tempest/openstack/common/lockutils.py b/tempest/openstack/common/lockutils.py
index 8ea8766..65f3548 100644
--- a/tempest/openstack/common/lockutils.py
+++ b/tempest/openstack/common/lockutils.py
@@ -20,6 +20,10 @@
import errno
import functools
import os
+import shutil
+import subprocess
+import sys
+import tempfile
import threading
import time
import weakref
@@ -39,6 +43,7 @@
cfg.BoolOpt('disable_process_locking', default=False,
help='Whether to disable inter-process locks'),
cfg.StrOpt('lock_path',
+ default=os.environ.get("TEMPEST_LOCK_PATH"),
help=('Directory to use for lock files.'))
]
@@ -131,6 +136,7 @@
InterProcessLock = _PosixLock
_semaphores = weakref.WeakValueDictionary()
+_semaphores_lock = threading.Lock()
@contextlib.contextmanager
@@ -153,15 +159,12 @@
special location for external lock files to live. If nothing is set, then
CONF.lock_path is used as a default.
"""
- # NOTE(soren): If we ever go natively threaded, this will be racy.
- # See http://stackoverflow.com/questions/5390569/dyn
- # amically-allocating-and-destroying-mutexes
- sem = _semaphores.get(name, threading.Semaphore())
- if name not in _semaphores:
- # this check is not racy - we're already holding ref locally
- # so GC won't remove the item and there was no IO switch
- # (only valid in greenthreads)
- _semaphores[name] = sem
+ with _semaphores_lock:
+ try:
+ sem = _semaphores[name]
+ except KeyError:
+ sem = threading.Semaphore()
+ _semaphores[name] = sem
with sem:
LOG.debug(_('Got semaphore "%(lock)s"'), {'lock': name})
@@ -276,3 +279,27 @@
"""
return functools.partial(synchronized, lock_file_prefix=lock_file_prefix)
+
+
+def main(argv):
+ """Create a dir for locks and pass it to command from arguments
+
+ If you run this:
+ python -m openstack.common.lockutils python setup.py testr <etc>
+
+ a temporary directory will be created for all your locks and passed to all
+ your tests in an environment variable. The temporary dir will be deleted
+ afterwards and the return value will be preserved.
+ """
+
+ lock_dir = tempfile.mkdtemp()
+ os.environ["TEMPEST_LOCK_PATH"] = lock_dir
+ try:
+ ret_val = subprocess.call(argv[1:])
+ finally:
+ shutil.rmtree(lock_dir, ignore_errors=True)
+ return ret_val
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 7848afc..06841e1 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -360,7 +360,8 @@
to become %s" % (thing_id, log_status)
raise exceptions.TimeoutException(message)
- def create_loginable_secgroup_rule(self, client=None, secgroup_id=None):
+ def _create_loginable_secgroup_rule_nova(self, client=None,
+ secgroup_id=None):
if client is None:
client = self.compute_client
if secgroup_id is None:
@@ -389,10 +390,13 @@
'cidr': '0.0.0.0/0',
}
]
+ rules = list()
for ruleset in rulesets:
sg_rule = client.security_group_rules.create(secgroup_id,
**ruleset)
self.set_resource(sg_rule.id, sg_rule)
+ rules.append(sg_rule)
+ return rules
def create_server(self, client=None, name=None, image=None, flavor=None,
create_kwargs={}):
@@ -506,22 +510,6 @@
cls.config.identity.password,
cls.config.identity.tenant_name).tenant_id
- def _create_security_group(self, client=None, namestart='secgroup-smoke-'):
- if client is None:
- client = self.compute_client
- # Create security group
- sg_name = data_utils.rand_name(namestart)
- sg_desc = sg_name + " description"
- secgroup = client.security_groups.create(sg_name, sg_desc)
- self.assertEqual(secgroup.name, sg_name)
- self.assertEqual(secgroup.description, sg_desc)
- self.set_resource(sg_name, secgroup)
-
- # Add rules to the security group
- self.create_loginable_secgroup_rule(client, secgroup.id)
-
- return secgroup
-
def _create_network(self, tenant_id, namestart='network-smoke-'):
name = data_utils.rand_name(namestart)
body = dict(
@@ -648,6 +636,171 @@
'Auth failure in connecting to %s@%s via ssh' %
(username, ip_address))
+ def _create_security_group_nova(self, client=None,
+ namestart='secgroup-smoke-',
+ tenant_id=None):
+ if client is None:
+ client = self.compute_client
+ # Create security group
+ sg_name = data_utils.rand_name(namestart)
+ sg_desc = sg_name + " description"
+ secgroup = client.security_groups.create(sg_name, sg_desc)
+ self.assertEqual(secgroup.name, sg_name)
+ self.assertEqual(secgroup.description, sg_desc)
+ self.set_resource(sg_name, secgroup)
+
+ # Add rules to the security group
+ self._create_loginable_secgroup_rule_nova(client, secgroup.id)
+
+ return secgroup
+
+ def _create_security_group_neutron(self, tenant_id, client=None,
+ namestart='secgroup-smoke-'):
+ if client is None:
+ client = self.network_client
+ secgroup = self._create_empty_security_group(namestart=namestart,
+ client=client,
+ tenant_id=tenant_id)
+
+ # Add rules to the security group
+ rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
+ for rule in rules:
+ self.assertEqual(tenant_id, rule.tenant_id)
+ self.assertEqual(secgroup.id, rule.security_group_id)
+ return secgroup
+
+ def _create_empty_security_group(self, tenant_id, client=None,
+ namestart='secgroup-smoke-'):
+ """Create a security group without rules.
+
+ Default rules will be created:
+ - IPv4 egress to any
+ - IPv6 egress to any
+
+ :param tenant_id: secgroup will be created in this tenant
+ :returns: DeletableSecurityGroup -- containing the secgroup created
+ """
+ if client is None:
+ client = self.network_client
+ sg_name = data_utils.rand_name(namestart)
+ sg_desc = sg_name + " description"
+ sg_dict = dict(name=sg_name,
+ description=sg_desc)
+ sg_dict['tenant_id'] = tenant_id
+ body = dict(security_group=sg_dict)
+ result = client.create_security_group(body=body)
+ secgroup = net_common.DeletableSecurityGroup(
+ client=client,
+ **result['security_group']
+ )
+ self.assertEqual(secgroup.name, sg_name)
+ self.assertEqual(tenant_id, secgroup.tenant_id)
+ self.assertEqual(secgroup.description, sg_desc)
+ self.set_resource(sg_name, secgroup)
+ return secgroup
+
+ def _default_security_group(self, tenant_id, client=None):
+ """Get default secgroup for given tenant_id.
+
+ :returns: DeletableSecurityGroup -- default secgroup for given tenant
+ """
+ if client is None:
+ client = self.network_client
+ sgs = [
+ sg for sg in client.list_security_groups().values()[0]
+ if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
+ ]
+ msg = "No default security group for tenant %s." % (tenant_id)
+ self.assertTrue(len(sgs) > 0, msg)
+ if len(sgs) > 1:
+ msg = "Found %d default security groups" % len(sgs)
+ raise exc.NeutronClientNoUniqueMatch(msg=msg)
+ return net_common.DeletableSecurityGroup(client=client,
+ **sgs[0])
+
+ def _create_security_group_rule(self, client=None, secgroup=None,
+ tenant_id=None, **kwargs):
+ """Create a rule from a dictionary of rule parameters.
+
+ Create a rule in a secgroup. if secgroup not defined will search for
+ default secgroup in tenant_id.
+
+ :param secgroup: type DeletableSecurityGroup.
+ :param secgroup_id: search for secgroup by id
+ default -- choose default secgroup for given tenant_id
+ :param tenant_id: if secgroup not passed -- the tenant in which to
+ search for default secgroup
+ :param kwargs: a dictionary containing rule parameters:
+ for example, to allow incoming ssh:
+ rule = {
+ direction: 'ingress'
+ protocol:'tcp',
+ port_range_min: 22,
+ port_range_max: 22
+ }
+ """
+ if client is None:
+ client = self.network_client
+ if secgroup is None:
+ secgroup = self._default_security_group(tenant_id)
+
+ ruleset = dict(security_group_id=secgroup.id,
+ tenant_id=secgroup.tenant_id,
+ )
+ ruleset.update(kwargs)
+
+ body = dict(security_group_rule=dict(ruleset))
+ sg_rule = client.create_security_group_rule(body=body)
+ sg_rule = net_common.DeletableSecurityGroupRule(
+ client=client,
+ **sg_rule['security_group_rule']
+ )
+ self.set_resource(sg_rule.id, sg_rule)
+ self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
+ self.assertEqual(secgroup.id, sg_rule.security_group_id)
+
+ return sg_rule
+
+ def _create_loginable_secgroup_rule_neutron(self, client=None,
+ secgroup=None):
+ """These rules are intended to permit inbound ssh and icmp
+ traffic from all sources, so no group_id is provided.
+ Setting a group_id would only permit traffic from ports
+ belonging to the same security group.
+ """
+
+ if client is None:
+ client = self.network_client
+ rules = []
+ rulesets = [
+ dict(
+ # ssh
+ protocol='tcp',
+ port_range_min=22,
+ port_range_max=22,
+ ),
+ dict(
+ # ping
+ protocol='icmp',
+ )
+ ]
+ for ruleset in rulesets:
+ for r_direction in ['ingress', 'egress']:
+ ruleset['direction'] = r_direction
+ try:
+ sg_rule = self._create_security_group_rule(
+ client=client, secgroup=secgroup, **ruleset)
+ except exc.NeutronClientException as ex:
+ # if rule already exist - skip rule and continue
+ if not (ex.status_code is 409 and 'Security group rule'
+ ' already exists' in ex.message):
+ raise ex
+ else:
+ self.assertEqual(r_direction, sg_rule.direction)
+ rules.append(sg_rule)
+
+ return rules
+
class OrchestrationScenarioTest(OfficialClientTest):
"""
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
index 30c223f..7f8d3e4 100644
--- a/tempest/scenario/test_large_ops.py
+++ b/tempest/scenario/test_large_ops.py
@@ -85,7 +85,7 @@
name = data_utils.rand_name('scenario-server-')
client = self.compute_client
flavor_id = self.config.compute.flavor_ref
- secgroup = self._create_security_group()
+ secgroup = self._create_security_group_nova()
self.servers = client.servers.create(
name=name, image=self.image,
flavor=flavor_id,
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 9cc8541..8a51cd1 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -161,7 +161,7 @@
self.nova_floating_ip_create()
self.nova_floating_ip_add()
- self.create_loginable_secgroup_rule()
+ self._create_loginable_secgroup_rule_nova()
self.ssh_to_server()
self.check_partitions()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index a7618b1..bfded53 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -164,7 +164,8 @@
name=data_utils.rand_name('keypair-smoke-'))
def _create_security_groups(self):
- self.security_groups[self.tenant_id] = self._create_security_group()
+ self.security_groups[self.tenant_id] =\
+ self._create_security_group_neutron(tenant_id=self.tenant_id)
def _create_networks(self):
network = self._create_network(self.tenant_id)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 0b08f9c..1e1a310 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -50,7 +50,7 @@
self.set_resource('secgroup', self.secgroup)
# Add rules to the security group
- self.create_loginable_secgroup_rule(secgroup_id=self.secgroup.id)
+ self._create_loginable_secgroup_rule_nova(secgroup_id=self.secgroup.id)
def boot_instance(self):
create_kwargs = {
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index ba347e0..00139f0 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -65,7 +65,7 @@
def test_snapshot_pattern(self):
# prepare for booting a instance
self._add_keypair()
- self.create_loginable_secgroup_rule()
+ self._create_loginable_secgroup_rule_nova()
# boot a instance and create a timestamp file in it
server = self._boot_image(self.config.compute.image_ref)
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index a8cedc7..5eac55c 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -146,7 +146,7 @@
def test_stamp_pattern(self):
# prepare for booting a instance
self._add_keypair()
- self.create_loginable_secgroup_rule()
+ self._create_loginable_secgroup_rule_nova()
# boot an instance and create a timestamp file in it
volume = self._create_volume()
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 84846c1..fa9a228 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -120,7 +120,7 @@
@services('compute', 'volume', 'image')
def test_volume_boot_pattern(self):
keypair = self.create_keypair()
- self.create_loginable_secgroup_rule()
+ self._create_loginable_secgroup_rule_nova()
# create an instance from volume
volume_origin = self._create_volume_from_image()
diff --git a/tempest/services/compute/xml/flavors_client.py b/tempest/services/compute/xml/flavors_client.py
index 363c1a8..a1c74d9 100644
--- a/tempest/services/compute/xml/flavors_client.py
+++ b/tempest/services/compute/xml/flavors_client.py
@@ -43,6 +43,10 @@
def _format_flavor(self, f):
flavor = {'links': []}
for k, v in f.items():
+ if k == 'id':
+ flavor['id'] = v
+ continue
+
if k == 'link':
flavor['links'].append(v)
continue
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index b4a1a68..967dc09 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -256,3 +256,10 @@
url = 'volumes/%s/action' % (volume_id)
resp, body = self.post(url, post_body, self.headers)
return resp, body
+
+ def force_delete_volume(self, volume_id):
+ """Force Delete Volume."""
+ post_body = json.dumps({'os-force_delete': {}})
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body,
+ self.headers)
+ return resp, body
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 21254aa..1fc63e9 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -347,3 +347,12 @@
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
+
+ def force_delete_volume(self, volume_id):
+ """Force Delete Volume."""
+ post_body = Element("os-force_delete")
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)), self.headers)
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
diff --git a/test-requirements.txt b/test-requirements.txt
index fbe7e43..41a784e 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,4 +1,4 @@
-hacking>=0.5.6,<0.8
+hacking>=0.8.0,<0.9
# needed for doc build
docutils==0.9.1
sphinx>=1.1.2
diff --git a/tools/check_logs.py b/tools/check_logs.py
index ded51b4..6d4436e 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -125,8 +125,8 @@
if is_neutron:
print("Currently not failing neutron builds with errors")
return 0
- # Return non-zero to start failing builds
- return 0
+ print("FAILED")
+ return 1
else:
print("ok")
return 0
diff --git a/tools/verify_tempest_config.py b/tools/verify_tempest_config.py
index 1b5fe68..347659d 100755
--- a/tools/verify_tempest_config.py
+++ b/tools/verify_tempest_config.py
@@ -36,11 +36,11 @@
__, versions = os.image_client.get_versions()
if CONF.image_feature_enabled.api_v1 != ('v1.1' in versions or 'v1.0' in
versions):
- print 'Config option image api_v1 should be change to: %s' % (
- not CONF.image_feature_enabled.api_v1)
+ print('Config option image api_v1 should be change to: %s' % (
+ not CONF.image_feature_enabled.api_v1))
if CONF.image_feature_enabled.api_v2 != ('v2.0' in versions):
- print 'Config option image api_v2 should be change to: %s' % (
- not CONF.image_feature_enabled.api_v2)
+ print('Config option image api_v2 should be change to: %s' % (
+ not CONF.image_feature_enabled.api_v2))
def verify_extensions(os):
@@ -62,8 +62,8 @@
for option in NOVA_EXTENSIONS.keys():
config_value = getattr(CONF.compute_feature_enabled, option)
if config_value != results['nova_features'][option]:
- print "Config option: %s should be changed to: %s" % (
- option, not config_value)
+ print("Config option: %s should be changed to: %s" % (
+ option, not config_value))
def main(argv):