Merge "Test cases for keystone tenant operations"
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/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/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_server_personality.py b/tempest/tests/test_server_personality.py
index 0d90857..c790a6c 100644
--- a/tempest/tests/test_server_personality.py
+++ b/tempest/tests/test_server_personality.py
@@ -41,22 +41,25 @@
Server should be created successfully if maximum allowed number of
files is injected into the server during creation.
"""
- name = rand_name('server')
- file_contents = 'This is a test file.'
+ try:
+ name = rand_name('server')
+ file_contents = 'This is a test file.'
- resp, max_file_limit = self.user_client.get_personality_file_limit()
- self.assertEqual(200, resp.status)
+ resp, max_file_limit = self.user_client.\
+ get_personality_file_limit()
+ self.assertEqual(200, resp.status)
- personality = []
- for i in range(0, max_file_limit):
- path = 'etc/test' + str(i) + '.txt'
- personality.append({'path': path,
+ personality = []
+ for i in range(0, max_file_limit):
+ path = 'etc/test' + str(i) + '.txt'
+ personality.append({'path': path,
'contents': base64.b64encode(file_contents)})
- resp, server = self.client.create_server(name, self.image_ref,
+ resp, server = self.client.create_server(name, self.image_ref,
self.flavor_ref,
personality=personality)
- self.assertEqual('202', resp['status'])
+ self.assertEqual('202', resp['status'])
#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