Merge "Disable test_run_terminate_instance"
diff --git a/.gitignore b/.gitignore
index b4dca86..c154603 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+AUTHORS
+ChangeLog
 *.pyc
 etc/tempest.conf
 include/swift_objects/swift_small
@@ -6,7 +8,7 @@
 *.log
 *.swp
 *.swo
-*.egg-info
+*.egg*
 .tox
 .venv
 dist
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..9a22c71
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,8 @@
+Ravikumar Venkatesan <ravikumar.venkatesan@hp.com> ravikumar-venkatesan <ravikumar.venkatesan@hp.com>
+Ravikumar Venkatesan <ravikumar.venkatesan@hp.com> ravikumar venkatesan <ravikumar.venkatesan@hp.com>
+Rohit Karajgi <rohit.karajgi@nttdata.com> Rohit Karajgi <rohit.karajgi@vertex.co.in>
+Jay Pipes <jaypipes@gmail.com> Jay Pipes <jpipes@librebox.gateway.2wire.net>
+<brian.waldon@rackspace.com> <bcwaldon@gmail.com>
+Daryl Walleck <daryl.walleck@rackspace.com> dwalleck <daryl.walleck@rackspace.com>
+<jeblair@hp.com> <corvus@inaugust.com>
+<jeblair@hp.com> <james.blair@rackspace.com>
diff --git a/tempest/services/compute/xml/images_client.py b/tempest/services/compute/xml/images_client.py
index 1e8b250..bde9e16 100644
--- a/tempest/services/compute/xml/images_client.py
+++ b/tempest/services/compute/xml/images_client.py
@@ -70,6 +70,13 @@
                              node.findall('{http://www.w3.org/2005/Atom}link')]
         return json
 
+    def _parse_images(self, xml):
+        json = {'images': []}
+        images = xml.getchildren()
+        for image in images:
+            json['images'].append(self._parse_image(image))
+        return json
+
     def create_image(self, server_id, name, meta=None):
         """Creates an image of the original server."""
         post_body = Element('createImage', name=name)
@@ -92,7 +99,7 @@
             url += '?%s' % urllib.urlencode(params)
 
         resp, body = self.get(url, self.headers)
-        body = xml_to_json(etree.fromstring(body))
+        body = self._parse_images(etree.fromstring(body))
         return resp, body['images']
 
     def list_images_with_detail(self, params=None):
@@ -104,7 +111,7 @@
             url = "images/detail?" + param_list
 
         resp, body = self.get(url, self.headers)
-        body = xml_to_json(etree.fromstring(body))
+        body = self._parse_images(etree.fromstring(body))
         return resp, body['images']
 
     def get_image(self, image_id):
diff --git a/tempest/services/identity/xml/admin_client.py b/tempest/services/identity/xml/admin_client.py
index 8448ae0..60897e9 100644
--- a/tempest/services/identity/xml/admin_client.py
+++ b/tempest/services/identity/xml/admin_client.py
@@ -136,6 +136,13 @@
         body = self._parse_array(etree.fromstring(body))
         return resp, body
 
+    def get_tenant_by_name(self, tenant_name):
+        resp, tenants = self.list_tenants()
+        for tenant in tenants:
+            if tenant['name'] == tenant_name:
+                return tenant
+        raise exceptions.NotFound('No such tenant')
+
     def update_tenant(self, tenant_id, **kwargs):
         """Updates a tenant."""
         resp, body = self.get_tenant(tenant_id)
@@ -198,6 +205,13 @@
         body = self._parse_array(etree.fromstring(body))
         return resp, body
 
+    def get_user_by_username(self, tenant_id, username):
+        resp, users = self.list_users_for_tenant(tenant_id)
+        for user in users:
+            if user['name'] == username:
+                return user
+        raise exceptions.NotFound('No such user')
+
     def create_service(self, name, type, **kwargs):
         """Create a service."""
         OS_KSADM = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0"
diff --git a/tempest/tests/compute/base.py b/tempest/tests/compute/base.py
index e315d78..8044d01 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -100,7 +100,7 @@
         operate in an isolated tenant container.
         """
         admin_client = cls._get_identity_admin_client()
-        rand_name_root = cls.__name__
+        rand_name_root = rand_name(cls.__name__)
         if cls.isolated_creds:
             # Main user already created. Create the alt one...
             rand_name_root += '-alt'
diff --git a/tempest/tests/compute/servers/test_server_actions.py b/tempest/tests/compute/servers/test_server_actions.py
index f4e62b1..91f0674 100644
--- a/tempest/tests/compute/servers/test_server_actions.py
+++ b/tempest/tests/compute/servers/test_server_actions.py
@@ -44,7 +44,7 @@
         self.client.wait_for_server_status(self.server_id, 'ACTIVE')
 
     def tearDown(self):
-        self.client.delete_server(self.server_id)
+        self.clear_servers()
 
     @attr(type='smoke')
     @unittest.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
@@ -67,11 +67,9 @@
         # The server should be power cycled
         if self.run_ssh:
             # Get the time the server was last rebooted,
-            # waiting for one minute as who doesn't have seconds precision
             resp, server = self.client.get_server(self.server_id)
             linux_client = RemoteClient(server, self.ssh_user, self.password)
             boot_time = linux_client.get_boot_time()
-            time.sleep(60)
 
         resp, body = self.client.reboot(self.server_id, 'HARD')
         self.assertEqual(202, resp.status)
@@ -89,11 +87,9 @@
         # The server should be signaled to reboot gracefully
         if self.run_ssh:
             # Get the time the server was last rebooted,
-            # waiting for one minute as who doesn't have seconds precision
             resp, server = self.client.get_server(self.server_id)
             linux_client = RemoteClient(server, self.ssh_user, self.password)
             boot_time = linux_client.get_boot_time()
-            time.sleep(60)
 
         resp, body = self.client.reboot(self.server_id, 'SOFT')
         self.assertEqual(202, resp.status)
diff --git a/tempest/tests/identity/admin/test_tenants.py b/tempest/tests/identity/admin/test_tenants.py
index 8fba7e3..578af4a 100644
--- a/tempest/tests/identity/admin/test_tenants.py
+++ b/tempest/tests/identity/admin/test_tenants.py
@@ -17,6 +17,7 @@
 
 import unittest2 as unittest
 
+from nose.plugins.attrib import attr
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.tests.identity import base
@@ -24,21 +25,6 @@
 
 class TenantsTestBase(object):
 
-    @staticmethod
-    def setUpClass(cls):
-        for _ in xrange(5):
-            resp, tenant = cls.client.create_tenant(rand_name('tenant-'))
-            cls.data.tenants.append(tenant)
-
-    def test_list_tenants(self):
-        # Return a list of all tenants
-        resp, body = self.client.list_tenants()
-        found = [tenant for tenant in body if tenant in self.data.tenants]
-        self.assertTrue(any(found), 'List did not return newly created '
-                        'tenants')
-        self.assertEqual(len(found), len(self.data.tenants))
-        self.assertTrue(resp['status'].startswith('2'))
-
     def test_list_tenants_by_unauthorized_user(self):
         # Non-admin user should not be able to list tenants
         self.assertRaises(exceptions.Unauthorized,
@@ -51,41 +37,50 @@
         self.assertRaises(exceptions.Unauthorized, self.client.list_tenants)
         self.client.clear_auth()
 
-    def test_tenant_delete(self):
+    def test_tenant_list_delete(self):
         # Create several tenants and delete them
         tenants = []
-        for _ in xrange(5):
-            resp, body = self.client.create_tenant(rand_name('tenant-new'))
-            tenants.append(body['id'])
-
+        for _ in xrange(3):
+            resp, tenant = self.client.create_tenant(rand_name('tenant-new'))
+            self.data.tenants.append(tenant)
+            tenants.append(tenant)
+        tenant_ids = map(lambda x: x['id'], tenants)
         resp, body = self.client.list_tenants()
-        found_1 = [tenant for tenant in body if tenant['id'] in tenants]
-        for tenant_id in tenants:
-            resp, body = self.client.delete_tenant(tenant_id)
+        self.assertTrue(resp['status'].startswith('2'))
+        found = [tenant for tenant in body if tenant['id'] in tenant_ids]
+        self.assertEqual(len(found), len(tenants), 'Tenants not created')
+
+        for tenant in tenants:
+            resp, body = self.client.delete_tenant(tenant['id'])
             self.assertTrue(resp['status'].startswith('2'))
+            self.data.tenants.remove(tenant)
 
         resp, body = self.client.list_tenants()
-        found_2 = [tenant for tenant in body if tenant['id'] in tenants]
-        self.assertTrue(any(found_1), 'Tenants not created')
-        self.assertFalse(any(found_2), 'Tenants failed to delete')
+        found = [tenant for tenant in body if tenant['id'] in tenant_ids]
+        self.assertFalse(any(found), 'Tenants failed to delete')
 
+    @attr(type='negative')
     def test_tenant_delete_by_unauthorized_user(self):
         # Non-admin user should not be able to delete a tenant
         tenant_name = rand_name('tenant-')
         resp, tenant = self.client.create_tenant(tenant_name)
+        self.data.tenants.append(tenant)
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_client.delete_tenant, tenant['id'])
 
+    @attr(type='negative')
     def test_tenant_delete_request_without_token(self):
         # Request to delete a tenant without a valid token should fail
         tenant_name = rand_name('tenant-')
         resp, tenant = self.client.create_tenant(tenant_name)
+        self.data.tenants.append(tenant)
         token = self.client.get_auth()
         self.client.delete_token(token)
         self.assertRaises(exceptions.Unauthorized, self.client.delete_tenant,
                           tenant['id'])
         self.client.clear_auth()
 
+    @attr(type='negative')
     def test_delete_non_existent_tenant(self):
         # Attempt to delete a non existent tenant should fail
         self.assertRaises(exceptions.NotFound, self.client.delete_tenant,
@@ -97,6 +92,8 @@
         tenant_desc = rand_name('desc-')
         resp, body = self.client.create_tenant(tenant_name,
                                                description=tenant_desc)
+        tenant = body
+        self.data.tenants.append(tenant)
         st1 = resp['status']
         tenant_id = body['id']
         desc1 = body['description']
@@ -108,11 +105,14 @@
         self.assertEqual(desc2, tenant_desc, 'Description does not appear'
                          'to be set')
         self.client.delete_tenant(tenant_id)
+        self.data.tenants.remove(tenant)
 
     def test_tenant_create_enabled(self):
         # Create a tenant that is enabled
         tenant_name = rand_name('tenant-')
         resp, body = self.client.create_tenant(tenant_name, enabled=True)
+        tenant = body
+        self.data.tenants.append(tenant)
         tenant_id = body['id']
         st1 = resp['status']
         en1 = body['enabled']
@@ -122,11 +122,14 @@
         en2 = body['enabled']
         self.assertTrue(en2, 'Enable should be True in lookup')
         self.client.delete_tenant(tenant_id)
+        self.data.tenants.remove(tenant)
 
     def test_tenant_create_not_enabled(self):
         # Create a tenant that is not enabled
         tenant_name = rand_name('tenant-')
         resp, body = self.client.create_tenant(tenant_name, enabled=False)
+        tenant = body
+        self.data.tenants.append(tenant)
         tenant_id = body['id']
         st1 = resp['status']
         en1 = body['enabled']
@@ -138,11 +141,15 @@
         self.assertEqual('false', str(en2).lower(),
                          'Enable should be False in lookup')
         self.client.delete_tenant(tenant_id)
+        self.data.tenants.remove(tenant)
 
+    @attr(type='negative')
     def test_tenant_create_duplicate(self):
         # Tenant names should be unique
         tenant_name = rand_name('tenant-dup-')
         resp, body = self.client.create_tenant(tenant_name)
+        tenant = body
+        self.data.tenants.append(tenant)
         tenant1_id = body.get('id')
 
         try:
@@ -151,15 +158,17 @@
             self.fail('Should not be able to create a duplicate tenant name')
         except exceptions.Duplicate:
             pass
-        if tenant1_id:
-            self.client.delete_tenant(tenant1_id)
+        self.client.delete_tenant(tenant1_id)
+        self.data.tenants.remove(tenant)
 
+    @attr(type='negative')
     def test_create_tenant_by_unauthorized_user(self):
         # Non-admin user should not be authorized to create a tenant
         tenant_name = rand_name('tenant-')
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_client.create_tenant, tenant_name)
 
+    @attr(type='negative')
     def test_create_tenant_request_without_token(self):
         # Create tenant request without a token should not be authorized
         tenant_name = rand_name('tenant-')
@@ -169,6 +178,7 @@
                           tenant_name)
         self.client.clear_auth()
 
+    @attr(type='negative')
     def test_create_tenant_with_empty_name(self):
         # Tenant name should not be empty
         self.assertRaises(exceptions.BadRequest, self.client.create_tenant,
@@ -184,6 +194,9 @@
         # Update name attribute of a tenant
         t_name1 = rand_name('tenant-')
         resp, body = self.client.create_tenant(t_name1)
+        tenant = body
+        self.data.tenants.append(tenant)
+
         t_id = body['id']
         resp1_name = body['name']
 
@@ -202,12 +215,16 @@
         self.assertEqual(resp2_name, resp3_name)
 
         self.client.delete_tenant(t_id)
+        self.data.tenants.remove(tenant)
 
     def test_tenant_update_desc(self):
         # Update description attribute of a tenant
         t_name = rand_name('tenant-')
         t_desc = rand_name('desc-')
         resp, body = self.client.create_tenant(t_name, description=t_desc)
+        tenant = body
+        self.data.tenants.append(tenant)
+
         t_id = body['id']
         resp1_desc = body['description']
 
@@ -226,12 +243,16 @@
         self.assertEqual(resp2_desc, resp3_desc)
 
         self.client.delete_tenant(t_id)
+        self.data.tenants.remove(tenant)
 
     def test_tenant_update_enable(self):
         # Update the enabled attribute of a tenant
         t_name = rand_name('tenant-')
         t_en = False
         resp, body = self.client.create_tenant(t_name, enabled=t_en)
+        tenant = body
+        self.data.tenants.append(tenant)
+
         t_id = body['id']
         resp1_en = body['enabled']
 
@@ -250,6 +271,7 @@
         self.assertEqual(resp2_en, resp3_en)
 
         self.client.delete_tenant(t_id)
+        self.data.tenants.remove(tenant)
 
 
 class TenantsTestJSON(base.BaseIdentityAdminTestJSON,
@@ -258,7 +280,6 @@
     @classmethod
     def setUpClass(cls):
         super(TenantsTestJSON, cls).setUpClass()
-        TenantsTestBase.setUpClass(cls)
 
 
 class TenantsTestXML(base.BaseIdentityAdminTestXML, TenantsTestBase):
@@ -266,4 +287,3 @@
     @classmethod
     def setUpClass(cls):
         super(TenantsTestXML, cls).setUpClass()
-        TenantsTestBase.setUpClass(cls)
diff --git a/tempest/tests/volume/test_volumes_actions.py b/tempest/tests/volume/test_volumes_actions.py
index 7eddb67..155acb6 100644
--- a/tempest/tests/volume/test_volumes_actions.py
+++ b/tempest/tests/volume/test_volumes_actions.py
@@ -46,7 +46,10 @@
         super(VolumesActionsTest, cls).tearDownClass()
         # Delete the test instance and volume
         cls.client.delete_volume(cls.volume['id'])
+        cls.client.wait_for_resource_deletion(cls.volume['id'])
+
         cls.servers_client.delete_server(cls.server['id'])
+        cls.client.wait_for_resource_deletion(cls.server['id'])
 
     @attr(type='smoke')
     def test_attach_detach_volume_to_instance(self):
diff --git a/tempest/tests/volume/test_volumes_list.py b/tempest/tests/volume/test_volumes_list.py
index 26a85b7..2fc1353 100644
--- a/tempest/tests/volume/test_volumes_list.py
+++ b/tempest/tests/volume/test_volumes_list.py
@@ -87,8 +87,9 @@
                     # 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_id_list:
-                        cls.client.delete_volume(volume)
+                    for volid in cls.volume_id_list:
+                        cls.client.delete_volume(volid)
+                        cls.client.wait_for_resource_deletion(volid)
                     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 "
@@ -99,9 +100,9 @@
     @classmethod
     def tearDownClass(cls):
         # Delete the created volumes
-        for volume in cls.volume_id_list:
-            resp, _ = cls.client.delete_volume(volume)
-            cls.client.wait_for_resource_deletion(volume)
+        for volid in cls.volume_id_list:
+            resp, _ = cls.client.delete_volume(volid)
+            cls.client.wait_for_resource_deletion(volid)
         super(VolumeListTestXML, cls).tearDownClass()
 
 
@@ -133,8 +134,9 @@
                     # 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_id_list:
-                        cls.client.delete_volume(volume)
+                    for volid in cls.volume_id_list:
+                        cls.client.delete_volume(volid)
+                        cls.client.wait_for_resource_deletion(volid)
                     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 "
@@ -145,7 +147,7 @@
     @classmethod
     def tearDownClass(cls):
         # Delete the created volumes
-        for volume in cls.volume_id_list:
-            resp, _ = cls.client.delete_volume(volume)
-            cls.client.wait_for_resource_deletion(volume)
+        for volid in cls.volume_id_list:
+            resp, _ = cls.client.delete_volume(volid)
+            cls.client.wait_for_resource_deletion(volid)
         super(VolumeListTestJSON, cls).tearDownClass()