Merge "Fixed the LP bug 993754. Ensure that the server created in the test is destroyed in finally: block of the test."
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 226fa30..aa101d3 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -3,6 +3,10 @@
# test clients use when authenticating with different user/tenant
# combinations
+# The type of endpoint for a Identity service. Unless you have a
+# custom Keystone service catalog implementation, you probably want to leave
+# this value as "identity"
+catalog_type = identity
# Set to True if your test environment's Keystone authentication service should
# be accessed over HTTPS
use_ssl = False
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index e9741bf..fd2f684 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -132,12 +132,15 @@
if mgmt_url == None:
raise exceptions.EndpointNotFound(service)
- #TODO (dwalleck): This is a horrible stopgap.
- #Need to join strings more cleanly
- temp = mgmt_url.rsplit('/')
- service_url = temp[0] + '//' + temp[2] + '/' + temp[3] + '/'
- management_url = service_url + tenant_id
- return token, management_url
+ if mgmt_url.endswith(tenant_id):
+ return token, mgmt_url
+ else:
+ #TODO (dwalleck): This is a horrible stopgap.
+ #Need to join strings more cleanly
+ temp = mgmt_url.rsplit('/')
+ service_url = temp[0] + '//' + temp[2] + '/' + temp[3] + '/'
+ management_url = service_url + tenant_id
+ return token, management_url
elif resp.status == 401:
raise exceptions.AuthenticationFailure(user=user,
password=password)
diff --git a/tempest/common/utils/data_utils.py b/tempest/common/utils/data_utils.py
index 2d540f4..752bc10 100644
--- a/tempest/common/utils/data_utils.py
+++ b/tempest/common/utils/data_utils.py
@@ -37,3 +37,10 @@
url += urllib.urlencode(params)
return url
+
+
+def parse_image_id(image_ref):
+ """Return the image id from a given image ref"""
+ temp = image_ref.rsplit('/')
+ #Return the last item, which is the image id
+ return temp[len(temp) - 1]
diff --git a/tempest/config.py b/tempest/config.py
index fcfeee5..d4a0c03 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -45,6 +45,11 @@
SECTION_NAME = "identity"
@property
+ def catalog_type(self):
+ """Catalog type of the Identity service."""
+ return self.get("catalog_type", 'identity')
+
+ @property
def host(self):
"""Host IP for making Identity API requests."""
return self.get("host", "127.0.0.1")
diff --git a/tempest/openstack.py b/tempest/openstack.py
index 491f385..3e19ba5 100644
--- a/tempest/openstack.py
+++ b/tempest/openstack.py
@@ -30,6 +30,7 @@
from tempest.services.nova.json.floating_ips_client import FloatingIPsClient
from tempest.services.nova.json.keypairs_client import KeyPairsClient
from tempest.services.nova.json.volumes_client import VolumesClient
+from tempest.services.identity.json.admin_client import AdminClient
LOG = logging.getLogger(__name__)
@@ -79,6 +80,7 @@
self.security_groups_client = SecurityGroupsClient(*client_args)
self.floating_ips_client = FloatingIPsClient(*client_args)
self.volumes_client = VolumesClient(*client_args)
+ self.admin_client = AdminClient(*client_args)
class AltManager(Manager):
diff --git a/tempest/services/identity/__init__.py b/tempest/services/identity/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/identity/__init__.py
diff --git a/tempest/services/identity/json/__init__.py b/tempest/services/identity/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/identity/json/__init__.py
diff --git a/tempest/services/identity/json/admin_client.py b/tempest/services/identity/json/admin_client.py
new file mode 100644
index 0000000..45a7985
--- /dev/null
+++ b/tempest/services/identity/json/admin_client.py
@@ -0,0 +1,44 @@
+from tempest.common.rest_client import RestClient
+import json
+
+
+class AdminClient(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(AdminClient, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.identity.catalog_type
+ self.endpoint_url = 'adminURL'
+
+ def has_admin_extensions(self):
+ """
+ Returns True if the KSADM Admin Extensions are supported
+ False otherwise
+ """
+ if hasattr(self, '_has_admin_extensions'):
+ return self._has_admin_extensions
+ resp, body = self.list_roles()
+ self._has_admin_extensions = ('status' in resp and resp.status != 503)
+ return self._has_admin_extensions
+
+ def create_role(self, name):
+ """Create a role"""
+ post_body = {
+ 'name': name,
+ }
+ post_body = json.dumps({'role': post_body})
+ resp, body = self.post('OS-KSADM/roles', post_body,
+ self.headers)
+ body = json.loads(body)
+ return resp, body['role']
+
+ def delete_role(self, role_id):
+ """Delete a role"""
+ resp, body = self.delete('OS-KSADM/roles/%s' % role_id)
+ return resp, body
+
+ def list_roles(self):
+ """Returns roles"""
+ resp, body = self.get('OS-KSADM/roles')
+ body = json.loads(body)
+ return resp, body['roles']
diff --git a/tempest/services/nova/json/flavors_client.py b/tempest/services/nova/json/flavors_client.py
index 84fa9ff..bd77484 100644
--- a/tempest/services/nova/json/flavors_client.py
+++ b/tempest/services/nova/json/flavors_client.py
@@ -39,3 +39,27 @@
resp, body = self.get("flavors/%s" % str(flavor_id))
body = json.loads(body)
return resp, body['flavor']
+
+ def create_flavor(self, name, ram, vcpus, disk, ephemeral, flavor_id,
+ swap, rxtx):
+ """Creates a new flavor or instance type"""
+ post_body = {
+ 'name': name,
+ 'ram': ram,
+ 'vcpus': vcpus,
+ 'disk': disk,
+ 'OS-FLV-EXT-DATA:ephemeral': ephemeral,
+ 'id': flavor_id,
+ 'swap': swap,
+ 'rxtx_factor': rxtx
+ }
+
+ post_body = json.dumps({'flavor': post_body})
+ resp, body = self.post('flavors', post_body, self.headers)
+
+ body = json.loads(body)
+ return resp, body['flavor']
+
+ def delete_flavor(self, flavor_id):
+ """Deletes the given flavor"""
+ return self.delete("flavors/%s" % str(flavor_id))
diff --git a/tempest/services/nova/json/servers_client.py b/tempest/services/nova/json/servers_client.py
index 08ac6df..53b2d6f 100644
--- a/tempest/services/nova/json/servers_client.py
+++ b/tempest/services/nova/json/servers_client.py
@@ -21,6 +21,7 @@
flavor_ref (Required): The flavor used to build the server.
Following optional keyword arguments are accepted:
adminPass: Sets the initial root password.
+ key_name: Key name of keypair that was created earlier.
meta: A dictionary of values to be used as metadata.
personality: A list of dictionaries for files to be injected into
the server.
@@ -40,7 +41,7 @@
'flavorRef': flavor_ref
}
- for option in ['personality', 'adminPass',
+ for option in ['personality', 'adminPass', 'key_name',
'security_groups', 'networks', 'user_data',
'availability_zone', 'accessIPv4', 'accessIPv6',
'min_count', 'max_count', ('metadata', 'meta'),
diff --git a/tempest/tests/compute/admin/__init__.py b/tempest/tests/compute/admin/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/compute/admin/__init__.py
diff --git a/tempest/tests/compute/admin/test_flavors.py b/tempest/tests/compute/admin/test_flavors.py
new file mode 100644
index 0000000..377781b
--- /dev/null
+++ b/tempest/tests/compute/admin/test_flavors.py
@@ -0,0 +1,111 @@
+from nose.plugins.attrib import attr
+from nose import SkipTest
+import tempest.config
+from tempest import exceptions
+from tempest import openstack
+from tempest.tests.base_compute_test import BaseComputeTest
+
+
+class FlavorsAdminTest(BaseComputeTest):
+
+ """
+ Tests Flavors API Create and Delete that require admin privileges
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ cls.config = tempest.config.TempestConfig()
+ cls.admin_username = cls.config.compute_admin.username
+ cls.admin_password = cls.config.compute_admin.password
+ cls.admin_tenant = cls.config.compute_admin.tenant_name
+
+ if not(cls.admin_username and cls.admin_password and cls.admin_tenant):
+ raise SkipTest("Missing Admin credentials in configuration")
+ else:
+ cls.admin_os = openstack.AdminManager()
+ cls.admin_client = cls.admin_os.flavors_client
+ cls.flavor_name = 'test_flavor'
+ cls.ram = 512
+ cls.vcpus = 1
+ cls.disk = 10
+ cls.ephemeral = 10
+ cls.new_flavor_id = 1234
+ cls.swap = 1024
+ cls.rxtx = 1
+
+ @attr(type='positive')
+ def test_create_flavor(self):
+ """Create a flavor and ensure it is listed
+ This operation requires the user to have 'admin' role"""
+
+ #Create the flavor
+ resp, flavor = self.admin_client.create_flavor(self.flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ self.ephemeral,
+ self.new_flavor_id,
+ self.swap, self.rxtx)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(flavor['name'], self.flavor_name)
+ self.assertEqual(flavor['vcpus'], self.vcpus)
+ self.assertEqual(flavor['disk'], self.disk)
+ self.assertEqual(flavor['ram'], self.ram)
+ self.assertEqual(int(flavor['id']), self.new_flavor_id)
+ self.assertEqual(flavor['swap'], self.swap)
+ self.assertEqual(flavor['rxtx_factor'], self.rxtx)
+ self.assertEqual(flavor['OS-FLV-EXT-DATA:ephemeral'], self.ephemeral)
+
+ #Verify flavor is retrieved
+ resp, flavor = self.admin_client.get_flavor_details(self.new_flavor_id)
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(flavor['name'], self.flavor_name)
+
+ #Delete the flavor
+ resp, body = self.admin_client.delete_flavor(flavor['id'])
+ self.assertEqual(resp.status, 202)
+
+ @attr(type='positive')
+ def test_create_flavor_verify_entry_in_list_details(self):
+ """Create a flavor and ensure it's details are listed
+ This operation requires the user to have 'admin' role"""
+
+ #Create the flavor
+ resp, flavor = self.admin_client.create_flavor(self.flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ self.ephemeral,
+ self.new_flavor_id,
+ self.swap, self.rxtx)
+ flag = False
+ #Verify flavor is retrieved
+ resp, flavors = self.admin_client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ for flavor in flavors:
+ if flavor['name'] == self.flavor_name:
+ flag = True
+ self.assertTrue(flag)
+
+ #Delete the flavor
+ resp, body = self.admin_client.delete_flavor(self.new_flavor_id)
+ self.assertEqual(resp.status, 202)
+
+ @attr(type='negative')
+ def test_get_flavor_details_for_deleted_flavor(self):
+ """Delete a flavor and ensure it is not listed"""
+
+ # Create a test flavor
+ resp, flavor = self.admin_client.create_flavor(self.flavor_name,
+ self.ram,
+ self.vcpus, self.disk,
+ self.ephemeral,
+ self.new_flavor_id,
+ self.swap, self.rxtx)
+ self.assertEquals(200, resp.status)
+
+ # Delete the flavor
+ resp, _ = self.admin_client.delete_flavor(self.new_flavor_id)
+ self.assertEqual(resp.status, 202)
+
+ # Get deleted flavor details
+ self.assertRaises(exceptions.NotFound,
+ self.admin_client.get_flavor_details, self.new_flavor_id)
diff --git a/tempest/tests/identity/__init__.py b/tempest/tests/identity/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/identity/__init__.py
diff --git a/tempest/tests/identity/test_roles.py b/tempest/tests/identity/test_roles.py
new file mode 100644
index 0000000..9f51505
--- /dev/null
+++ b/tempest/tests/identity/test_roles.py
@@ -0,0 +1,84 @@
+import unittest2 as unittest
+
+import nose
+
+from tempest import openstack
+from tempest import exceptions
+from tempest.common.utils.data_utils import rand_name
+from tempest.tests import utils
+
+
+class RolesTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.os = openstack.AdminManager()
+ cls.client = cls.os.admin_client
+ cls.config = cls.os.config
+
+ if not cls.client.has_admin_extensions():
+ raise nose.SkipTest("Admin extensions disabled")
+
+ cls.roles = []
+ for _ in xrange(5):
+ resp, body = cls.client.create_role(rand_name('role-'))
+ cls.roles.append(body['id'])
+
+ @classmethod
+ def tearDownClass(cls):
+ for role in cls.roles:
+ cls.client.delete_role(role)
+
+ def test_list_roles(self):
+ """Return a list of all roles"""
+ resp, body = self.client.list_roles()
+ found = [role for role in body if role['id'] in self.roles]
+ self.assertTrue(any(found))
+ self.assertEqual(len(found), len(self.roles))
+
+ def test_role_create_delete(self):
+ """Role should be created, verified, and deleted"""
+ role_name = rand_name('role-test-')
+ resp, body = self.client.create_role(role_name)
+ self.assertTrue('status' in resp)
+ self.assertTrue(resp['status'].startswith('2'))
+ self.assertEqual(role_name, body['name'])
+
+ resp, body = self.client.list_roles()
+ found = [role for role in body if role['name'] == role_name]
+ self.assertTrue(any(found))
+
+ resp, body = self.client.delete_role(found[0]['id'])
+ self.assertTrue('status' in resp)
+ self.assertTrue(resp['status'].startswith('2'))
+
+ resp, body = self.client.list_roles()
+ found = [role for role in body if role['name'] == role_name]
+ self.assertFalse(any(found))
+
+ def test_role_create_blank_name(self):
+ """Should not be able to create a role with a blank name"""
+ try:
+ resp, body = self.client.create_role('')
+ except exceptions.Duplicate:
+ self.fail('A role with a blank name already exists.')
+ self.assertTrue('status' in resp)
+ self.assertFalse(resp['status'].startswith('2'), 'Create role with '
+ 'empty name should fail')
+
+ def test_role_create_duplicate(self):
+ """Role names should be unique"""
+ role_name = rand_name('role-dup-')
+ resp, body = self.client.create_role(role_name)
+ role1_id = body.get('id')
+ self.assertTrue('status' in resp)
+ self.assertTrue(resp['status'].startswith('2'))
+
+ try:
+ resp, body = self.client.create_role(role_name)
+ # this should raise an exception
+ self.fail('Should not be able to create a duplicate role name.'
+ ' %s' % role_name)
+ except exceptions.Duplicate:
+ pass
+ self.client.delete_role(role1_id)
diff --git a/tempest/tests/test_authorization.py b/tempest/tests/test_authorization.py
index 9f3c34d..e66446d 100644
--- a/tempest/tests/test_authorization.py
+++ b/tempest/tests/test_authorization.py
@@ -4,9 +4,7 @@
from nose.tools import raises
from tempest import openstack
-from tempest.services.nova.json.images_client import ImagesClient
-from tempest.services.nova.json.servers_client import ServersClient
-from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils.data_utils import rand_name, parse_image_id
from tempest import exceptions
from tempest.tests import utils
@@ -53,7 +51,7 @@
name = rand_name('image')
resp, body = cls.client.create_image(server['id'], name)
- image_id = cls._parse_image_id(resp['location'])
+ image_id = parse_image_id(resp['location'])
cls.images_client.wait_for_image_resp_code(image_id, 200)
cls.images_client.wait_for_image_status(image_id, 'ACTIVE')
resp, cls.image = cls.images_client.get_image(image_id)
@@ -162,9 +160,3 @@
finally:
# Reset the base_url...
self.other_client.base_url = saved_base_url
-
- @classmethod
- def _parse_image_id(self, image_ref):
- temp = image_ref.rsplit('/')
- #Return the last item, which is the image id
- return temp[len(temp) - 1]
diff --git a/tempest/tests/test_flavors.py b/tempest/tests/test_flavors.py
index 34aa68c..d5d598f 100644
--- a/tempest/tests/test_flavors.py
+++ b/tempest/tests/test_flavors.py
@@ -1,8 +1,5 @@
-import unittest2 as unittest
from nose.plugins.attrib import attr
from tempest import exceptions
-from tempest import openstack
-import tempest.config
from base_compute_test import BaseComputeTest
@@ -11,13 +8,12 @@
@classmethod
def setUpClass(cls):
cls.client = cls.flavors_client
- cls.flavor_id = cls.flavor_ref
@attr(type='smoke')
def test_list_flavors(self):
"""List of all flavors should contain the expected flavor"""
resp, flavors = self.client.list_flavors()
- resp, flavor = self.client.get_flavor_details(self.flavor_id)
+ resp, flavor = self.client.get_flavor_details(self.flavor_ref)
flavor_min_detail = {'id': flavor['id'], 'links': flavor['links'],
'name': flavor['name']}
self.assertTrue(flavor_min_detail in flavors)
@@ -26,14 +22,14 @@
def test_list_flavors_with_detail(self):
"""Detailed list of all flavors should contain the expected flavor"""
resp, flavors = self.client.list_flavors_with_detail()
- resp, flavor = self.client.get_flavor_details(self.flavor_id)
+ resp, flavor = self.client.get_flavor_details(self.flavor_ref)
self.assertTrue(flavor in flavors)
@attr(type='smoke')
def test_get_flavor(self):
"""The expected flavor details should be returned"""
- resp, flavor = self.client.get_flavor_details(self.flavor_id)
- self.assertEqual(self.flavor_id, str(flavor['id']))
+ resp, flavor = self.client.get_flavor_details(self.flavor_ref)
+ self.assertEqual(self.flavor_ref, str(flavor['id']))
@attr(type='negative')
def test_get_non_existant_flavor(self):
@@ -120,3 +116,10 @@
params = {'minRam': flavors[1]['ram']}
resp, flavors = self.client.list_flavors(params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+
+ @attr(type='negative')
+ def test_get_flavor_details_for_invalid_flavor_id(self):
+ """Ensure 404 returned for non-existant flavor ID"""
+
+ self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
+ 9999)
diff --git a/tempest/tests/test_floating_ips_actions.py b/tempest/tests/test_floating_ips_actions.py
index a9222a4..563d0e3 100644
--- a/tempest/tests/test_floating_ips_actions.py
+++ b/tempest/tests/test_floating_ips_actions.py
@@ -48,16 +48,18 @@
Positive test:Allocation of a new floating IP to a project
should be succesfull
"""
- resp, body = self.client.create_floating_ip()
- self.assertEqual(200, resp.status)
- floating_ip_id_allocated = body['id']
- resp, floating_ip_details = \
+ try:
+ resp, body = self.client.create_floating_ip()
+ self.assertEqual(200, resp.status)
+ floating_ip_id_allocated = body['id']
+ resp, floating_ip_details = \
self.client.get_floating_ip_details(floating_ip_id_allocated)
- #Checking if the details of allocated IP is in list of floating IP
- resp, body = self.client.list_floating_ips()
- self.assertTrue(floating_ip_details in body)
- #Deleting the floating IP which is created in this method
- self.client.delete_floating_ip(floating_ip_id_allocated)
+ #Checking if the details of allocated IP is in list of floating IP
+ resp, body = self.client.list_floating_ips()
+ self.assertTrue(floating_ip_details in body)
+ finally:
+ #Deleting the floating IP which is created in this method
+ self.client.delete_floating_ip(floating_ip_id_allocated)
@attr(type='positive')
def test_delete_floating_ip(self):
diff --git a/tempest/tests/test_images.py b/tempest/tests/test_images.py
index 716da65..33020d6 100644
--- a/tempest/tests/test_images.py
+++ b/tempest/tests/test_images.py
@@ -5,11 +5,7 @@
from base_compute_test import BaseComputeTest
import tempest.config
from tempest import openstack
-
-
-def _parse_image_id(image_ref):
- temp = image_ref.rsplit('images/')
- return temp[1]
+from tempest.common.utils import data_utils
class ImagesTest(BaseComputeTest):
@@ -38,7 +34,7 @@
name = rand_name('image')
meta = {'image_type': 'test'}
resp, body = self.client.create_image(server['id'], name, meta)
- image_id = _parse_image_id(resp['location'])
+ image_id = data_utils.parse_image_id(resp['location'])
self.client.wait_for_image_resp_code(image_id, 200)
self.client.wait_for_image_status(image_id, 'ACTIVE')
@@ -78,7 +74,7 @@
pass
else:
- image_id = _parse_image_id(resp['location'])
+ image_id = data_utils.parse_image_id(resp['location'])
self.client.wait_for_image_resp_code(image_id, 200)
self.client.wait_for_image_status(image_id, 'ACTIVE')
self.client.delete_image(image_id)
diff --git a/tempest/tests/test_list_images.py b/tempest/tests/test_list_images.py
index 9d2bb1b..810e933 100644
--- a/tempest/tests/test_list_images.py
+++ b/tempest/tests/test_list_images.py
@@ -1,14 +1,8 @@
from nose.plugins.attrib import attr
from tempest import exceptions
-from tempest import openstack
from base_compute_test import BaseComputeTest
-from tempest.common.utils.data_utils import rand_name
-
-
-def _parse_image_id(image_ref):
- temp = image_ref.rsplit('/')
- return temp[len(temp) - 1]
+from tempest.common.utils.data_utils import rand_name, parse_image_id
class ListImagesTest(BaseComputeTest):
@@ -31,7 +25,7 @@
# Create images to be used in the filter tests
image1_name = rand_name('image')
resp, body = cls.client.create_image(cls.server1['id'], image1_name)
- cls.image1_id = _parse_image_id(resp['location'])
+ cls.image1_id = parse_image_id(resp['location'])
cls.client.wait_for_image_resp_code(cls.image1_id, 200)
cls.client.wait_for_image_status(cls.image1_id, 'ACTIVE')
resp, cls.image1 = cls.client.get_image(cls.image1_id)
@@ -41,14 +35,14 @@
# server will sometimes cause failures
image3_name = rand_name('image')
resp, body = cls.client.create_image(cls.server2['id'], image3_name)
- cls.image3_id = _parse_image_id(resp['location'])
+ cls.image3_id = parse_image_id(resp['location'])
cls.client.wait_for_image_resp_code(cls.image3_id, 200)
cls.client.wait_for_image_status(cls.image3_id, 'ACTIVE')
resp, cls.image3 = cls.client.get_image(cls.image3_id)
image2_name = rand_name('image')
resp, body = cls.client.create_image(cls.server1['id'], image2_name)
- cls.image2_id = _parse_image_id(resp['location'])
+ cls.image2_id = parse_image_id(resp['location'])
cls.client.wait_for_image_resp_code(cls.image2_id, 200)
cls.client.wait_for_image_status(cls.image2_id, 'ACTIVE')
resp, cls.image2 = cls.client.get_image(cls.image2_id)
diff --git a/tempest/tests/test_servers.py b/tempest/tests/test_servers.py
index 414d58a..b152b4c 100644
--- a/tempest/tests/test_servers.py
+++ b/tempest/tests/test_servers.py
@@ -54,58 +54,65 @@
password should be set to that password.
"""
- name = rand_name('server')
- resp, server = self.client.create_server(name, self.image_ref,
+ try:
+ name = rand_name('server')
+ resp, server = self.client.create_server(name, self.image_ref,
self.flavor_ref,
adminPass='testpassword')
- #Verify the password is set correctly in the response
- self.assertEqual('testpassword', server['adminPass'])
+ #Verify the password is set correctly in the response
+ self.assertEqual('testpassword', server['adminPass'])
#Teardown
- self.client.delete_server(server['id'])
+ finally:
+ self.client.delete_server(server['id'])
@attr(type='smoke')
def test_update_server_name(self):
"""The server name should be changed to the the provided value"""
- name = rand_name('server')
- resp, server = self.client.create_server(name, self.image_ref,
+ try:
+ name = rand_name('server')
+ resp, server = self.client.create_server(name, self.image_ref,
self.flavor_ref)
- self.client.wait_for_server_status(server['id'], 'ACTIVE')
+ self.client.wait_for_server_status(server['id'], 'ACTIVE')
- #Update the server with a new name
- resp, server = self.client.update_server(server['id'], name='newname')
- self.assertEquals(200, resp.status)
- self.client.wait_for_server_status(server['id'], 'ACTIVE')
+ #Update the server with a new name
+ resp, server = self.client.update_server(server['id'],
+ name='newname')
+ self.assertEquals(200, resp.status)
+ self.client.wait_for_server_status(server['id'], 'ACTIVE')
- #Verify the name of the server has changed
- resp, server = self.client.get_server(server['id'])
- self.assertEqual('newname', server['name'])
+ #Verify the name of the server has changed
+ resp, server = self.client.get_server(server['id'])
+ self.assertEqual('newname', server['name'])
#Teardown
- self.client.delete_server(server['id'])
+ finally:
+ self.client.delete_server(server['id'])
@attr(type='smoke')
def test_update_access_server_address(self):
"""
The server's access addresses should reflect the provided values
"""
- name = rand_name('server')
- resp, server = self.client.create_server(name, self.image_ref,
+ try:
+ name = rand_name('server')
+ resp, server = self.client.create_server(name, self.image_ref,
self.flavor_ref)
- self.client.wait_for_server_status(server['id'], 'ACTIVE')
+ self.client.wait_for_server_status(server['id'], 'ACTIVE')
- #Update the IPv4 and IPv6 access addresses
- resp, body = self.client.update_server(server['id'],
+ #Update the IPv4 and IPv6 access addresses
+ resp, body = self.client.update_server(server['id'],
accessIPv4='1.1.1.1',
accessIPv6='::babe:2.2.2.2')
- self.assertEqual(200, resp.status)
- self.client.wait_for_server_status(server['id'], 'ACTIVE')
+ self.assertEqual(200, resp.status)
+ self.client.wait_for_server_status(server['id'], 'ACTIVE')
- #Verify the access addresses have been updated
- resp, server = self.client.get_server(server['id'])
- self.assertEqual('1.1.1.1', server['accessIPv4'])
- self.assertEqual('::babe:2.2.2.2', server['accessIPv6'])
+ #Verify the access addresses have been updated
+ resp, server = self.client.get_server(server['id'])
+ self.assertEqual('1.1.1.1', server['accessIPv4'])
+ self.assertEqual('::babe:2.2.2.2', server['accessIPv6'])
#Teardown
- self.client.delete_server(server['id'])
+ finally:
+ self.client.delete_server(server['id'])
diff --git a/tempest/tests/test_volumes_list.py b/tempest/tests/test_volumes_list.py
index 01ce394..0a2e4e6 100644
--- a/tempest/tests/test_volumes_list.py
+++ b/tempest/tests/test_volumes_list.py
@@ -1,43 +1,87 @@
-from nose.plugins.attrib import attr
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack, LLC
+# 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 unittest2 as unittest
+
+import nose
+
from tempest import openstack
from tempest.common.utils.data_utils import rand_name
class VolumesTest(unittest.TestCase):
+ """
+ This test creates a number of 1G volumes. To run successfully,
+ ensure that the backing file for the volume group that Nova uses
+ has space for at least 3 1G volumes! Devstack, by default, creates
+ a 2G volume backing file, which causes this test to fail because
+ the third volume gets created in ERROR state (out of disk space in
+ volume group...). If you are running a Devstack environment, set
+ VOLUME_BACKING_FILE_SIZE=4G in your localrc
+ """
+
@classmethod
def setUpClass(cls):
cls.os = openstack.Manager()
cls.client = cls.os.volumes_client
- #Create 3 Volumes
+ # Create 3 Volumes
cls.volume_list = list()
cls.volume_id_list = list()
for i in range(3):
- v_name = rand_name('Name-')
+ v_name = rand_name('volume')
metadata = {'Type': 'work'}
- resp, volume = cls.client.create_volume(size=1,
- display_name=v_name,
- metadata=metadata)
- cls.client.wait_for_volume_status(volume['id'],
- 'available')
- resp, volume = cls.client.get_volume(volume['id'])
- cls.volume_list.append(volume)
- cls.volume_id_list.append(volume['id'])
+ try:
+ resp, volume = cls.client.create_volume(size=1,
+ display_name=v_name,
+ metadata=metadata)
+ cls.client.wait_for_volume_status(volume['id'],
+ 'available')
+ resp, volume = cls.client.get_volume(volume['id'])
+ cls.volume_list.append(volume)
+ cls.volume_id_list.append(volume['id'])
+ except:
+ if cls.volume_list:
+ # We could not create all the volumes, though we were able
+ # to create *some* of the volumes. This is typically
+ # because the backing file size of the volume group is
+ # too small. So, here, we clean up whatever we did manage
+ # to create and raise a SkipTest
+ for volume in cls.volume_list:
+ cls.client.delete_volume(volume)
+ msg = ("Failed to create ALL necessary volumes to run "
+ "test. This typically means that the backing file "
+ "size of the nova-volumes group is too small to "
+ "create the 3 volumes needed by this test case")
+ raise nose.SkipTest(msg)
+ raise
@classmethod
def tearDownClass(cls):
- #Delete the created Volumes
- for i in range(3):
- resp, _ = cls.client.delete_volume(cls.volume_id_list[i])
+ # Delete the created Volumes
+ for volume in cls.volume_list:
+ resp, _ = cls.client.delete_volume(volume['id'])
- @attr(type='smoke')
def test_volume_list(self):
"""Should return the list of Volumes"""
- #Fetch all Volumes
+ # Fetch all Volumes
resp, fetched_list = self.client.list_volumes()
self.assertEqual(200, resp.status)
- #Now check if all the Volumes created in setup are in fetched list
+ # Now check if all the Volumes created in setup are in fetched list
missing_volumes =\
[v for v in self.volume_list if v not in fetched_list]
self.assertFalse(missing_volumes,
@@ -45,7 +89,6 @@
% ', '.join(m_vol['displayName']
for m_vol in missing_volumes))
- @attr(type='smoke')
def test_volume_list_with_details(self):
"""Should return the list of Volumes with details"""
#Fetch all Volumes