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