Merge "add tests for server_password"
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 4d2bdbc..715ec16 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -158,6 +158,7 @@
         cls.services_client = cls.os.services_client
         cls.hypervisor_client = cls.os.hypervisor_client
         cls.servers_client_v3_auth = cls.os.servers_client_v3_auth
+        cls.certificates_client = cls.os.certificates_client
 
     @classmethod
     def create_image_from_server(cls, server_id, **kwargs):
@@ -230,6 +231,7 @@
         cls.servers_client = cls.os.servers_v3_client
         cls.images_client = cls.os.image_client
         cls.services_client = cls.os.services_v3_client
+        cls.extensions_client = cls.os.extensions_v3_client
 
     @classmethod
     def create_image_from_server(cls, server_id, **kwargs):
@@ -261,7 +263,7 @@
             pass
         resp, server = cls.create_test_server(wait_until='ACTIVE', **kwargs)
         cls.server_id = server['id']
-        cls.password = server['admin_pass']
+        cls.password = server['admin_password']
 
 
 class BaseV3ComputeAdminTest(BaseV3ComputeTest):
diff --git a/tempest/api/compute/certificates/__init__.py b/tempest/api/compute/certificates/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/compute/certificates/__init__.py
diff --git a/tempest/api/compute/certificates/test_certificates.py b/tempest/api/compute/certificates/test_certificates.py
new file mode 100644
index 0000000..4be1cff
--- /dev/null
+++ b/tempest/api/compute/certificates/test_certificates.py
@@ -0,0 +1,40 @@
+# 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.
+
+from tempest.api.compute import base
+from tempest.test import attr
+
+
+class CertificatesTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    @attr(type='gate')
+    def test_create_and_get_root_certificate(self):
+        # create certificates
+        resp, create_body = self.certificates_client.create_certificate()
+        self.assertEqual(200, resp.status)
+        self.assertIn('data', create_body)
+        self.assertIn('private_key', create_body)
+        # get the root certificate
+        resp, body = self.certificates_client.get_certificate('root')
+        self.assertEqual(200, resp.status)
+        self.assertIn('data', body)
+        self.assertIn('private_key', body)
+
+
+class CertificatesTestXML(CertificatesTestJSON):
+    _interface = 'xml'
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 21a2256..a06309a 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -37,7 +37,6 @@
         # Server creation
         resp, server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
-        resp, body = cls.servers_client.get_server(server['id'])
         # Floating IP creation
         resp, body = cls.client.create_floating_ip()
         cls.floating_ip_id = body['id']
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index 7fec2d1..6387f4e 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -32,7 +32,6 @@
         cls.client = cls.floating_ips_client
         cls.floating_ip = []
         cls.floating_ip_id = []
-        cls.random_number = 0
         for i in range(3):
             resp, body = cls.client.create_floating_ip()
             cls.floating_ip.append(body)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 961737a..bc53862 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -22,6 +22,7 @@
 
 from tempest.api import compute
 from tempest.api.compute import base
+from tempest.common.utils.data_utils import parse_image_id
 from tempest.common.utils.data_utils import rand_name
 from tempest.common.utils.linux.remote_client import RemoteClient
 import tempest.config
@@ -198,6 +199,75 @@
                 required time (%s s).' % (self.server_id, self.build_timeout)
                 raise exceptions.TimeoutException(message)
 
+    @skip_because(bug="1251920")
+    @attr(type='gate')
+    def test_create_backup(self):
+        # Positive test:create backup successfully and rotate backups correctly
+        # create the first and the second backup
+        backup1 = rand_name('backup')
+        resp, _ = self.servers_client.create_backup(self.server_id,
+                                                    'daily',
+                                                    2,
+                                                    backup1)
+        oldest_backup_exist = True
+
+        # the oldest one should be deleted automatically in this test
+        def _clean_oldest_backup(oldest_backup):
+            if oldest_backup_exist:
+                self.os.image_client.delete_image(oldest_backup)
+
+        image1_id = parse_image_id(resp['location'])
+        self.addCleanup(_clean_oldest_backup, image1_id)
+        self.assertEqual(202, resp.status)
+
+        backup2 = rand_name('backup')
+        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+        resp, _ = self.servers_client.create_backup(self.server_id,
+                                                    'daily',
+                                                    2,
+                                                    backup2)
+        image2_id = parse_image_id(resp['location'])
+        self.addCleanup(self.os.image_client.delete_image, image2_id)
+        self.assertEqual(202, resp.status)
+
+        # verify they have been created
+        properties = {
+            'image_type': 'backup',
+            'backup_type': "daily",
+            'instance_uuid': self.server_id,
+        }
+        resp, image_list = self.os.image_client.image_list_detail(
+            properties,
+            sort_key='created_at',
+            sort_dir='asc')
+        self.assertEqual(200, resp.status)
+        self.assertEqual(2, len(image_list))
+        self.assertEqual((backup1, backup2),
+                         (image_list[0]['name'], image_list[1]['name']))
+
+        # create the third one, due to the rotation is 2,
+        # the first one will be deleted
+        backup3 = rand_name('backup')
+        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+        resp, _ = self.servers_client.create_backup(self.server_id,
+                                                    'daily',
+                                                    2,
+                                                    backup3)
+        image3_id = parse_image_id(resp['location'])
+        self.addCleanup(self.os.image_client.delete_image, image3_id)
+        self.assertEqual(202, resp.status)
+        # the first back up should be deleted
+        self.os.image_client.wait_for_resource_deletion(image1_id)
+        oldest_backup_exist = False
+        resp, image_list = self.os.image_client.image_list_detail(
+            properties,
+            sort_key='created_at',
+            sort_dir='asc')
+        self.assertEqual(200, resp.status)
+        self.assertEqual(2, len(image_list))
+        self.assertEqual((backup2, backup3),
+                         (image_list[0]['name'], image_list[1]['name']))
+
     @attr(type='gate')
     def test_get_console_output(self):
         # Positive test:Should be able to GET the console output
@@ -245,6 +315,31 @@
         self.client.wait_for_server_status(self.server_id, 'ACTIVE')
 
     @attr(type='gate')
+    def test_shelve_unshelve_server(self):
+        resp, server = self.client.shelve_server(self.server_id)
+        self.assertEqual(202, resp.status)
+
+        offload_time = self.config.compute.shelved_offload_time
+        if offload_time >= 0:
+            self.client.wait_for_server_status(self.server_id,
+                                               'SHELVED_OFFLOADED',
+                                               extra_timeout=offload_time)
+        else:
+            self.client.wait_for_server_status(self.server_id,
+                                               'SHELVED')
+
+        resp, server = self.client.get_server(self.server_id)
+        image_name = server['name'] + '-shelved'
+        params = {'name': image_name}
+        resp, images = self.images_client.list_images(params)
+        self.assertEqual(1, len(images))
+        self.assertEqual(image_name, images[0]['name'])
+
+        resp, server = self.client.unshelve_server(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+
+    @attr(type='gate')
     def test_stop_start_server(self):
         resp, server = self.servers_client.stop(self.server_id)
         self.assertEqual(202, resp.status)
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 7eb127d..b12afd1 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -396,6 +396,54 @@
                           self.client.restore_soft_deleted_server,
                           self.server_id)
 
+    @attr(type=['negative', 'gate'])
+    def test_shelve_non_existent_server(self):
+        # shelve a non existent server
+        nonexistent_server = str(uuid.uuid4())
+        self.assertRaises(exceptions.NotFound, self.client.shelve_server,
+                          nonexistent_server)
+
+    @attr(type=['negative', 'gate'])
+    def test_shelve_shelved_server(self):
+        # shelve a shelved server.
+        resp, server = self.client.shelve_server(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.addCleanup(self.client.unshelve_server, self.server_id)
+
+        offload_time = self.config.compute.shelved_offload_time
+        if offload_time >= 0:
+            self.client.wait_for_server_status(self.server_id,
+                                               'SHELVED_OFFLOADED',
+                                               extra_timeout=offload_time)
+        else:
+            self.client.wait_for_server_status(self.server_id,
+                                               'SHELVED')
+
+        resp, server = self.client.get_server(self.server_id)
+        image_name = server['name'] + '-shelved'
+        params = {'name': image_name}
+        resp, images = self.images_client.list_images(params)
+        self.assertEqual(1, len(images))
+        self.assertEqual(image_name, images[0]['name'])
+
+        self.assertRaises(exceptions.Conflict,
+                          self.client.shelve_server,
+                          self.server_id)
+
+    @attr(type=['negative', 'gate'])
+    def test_unshelve_non_existent_server(self):
+        # unshelve a non existent server
+        nonexistent_server = str(uuid.uuid4())
+        self.assertRaises(exceptions.NotFound, self.client.unshelve_server,
+                          nonexistent_server)
+
+    @attr(type=['negative', 'gate'])
+    def test_unshelve_server_invalid_state(self):
+        # unshelve an active server.
+        self.assertRaises(exceptions.Conflict,
+                          self.client.unshelve_server,
+                          self.server_id)
+
 
 class ServersNegativeTestXML(ServersNegativeTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index fb4214a..8ac1f12 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -122,7 +122,7 @@
                                                    name=new_name,
                                                    metadata=meta,
                                                    personality=personality,
-                                                   admin_pass=password)
+                                                   admin_password=password)
 
         # Verify the properties in the initial response are correct
         self.assertEqual(self.server_id, rebuilt_server['id'])
diff --git a/tempest/api/compute/v3/test_extensions.py b/tempest/api/compute/v3/test_extensions.py
index 8f1e446..d7269d1 100644
--- a/tempest/api/compute/v3/test_extensions.py
+++ b/tempest/api/compute/v3/test_extensions.py
@@ -20,7 +20,7 @@
 from tempest.test import attr
 
 
-class ExtensionsTestJSON(base.BaseV2ComputeTest):
+class ExtensionsV3TestJSON(base.BaseV3ComputeTest):
     _interface = 'json'
 
     @attr(type='gate')
@@ -31,5 +31,5 @@
         self.assertEqual(200, resp.status)
 
 
-class ExtensionsTestXML(ExtensionsTestJSON):
+class ExtensionsV3TestXML(ExtensionsV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/identity/admin/test_roles.py b/tempest/api/identity/admin/test_roles.py
index 543cd91..8205d15 100644
--- a/tempest/api/identity/admin/test_roles.py
+++ b/tempest/api/identity/admin/test_roles.py
@@ -16,8 +16,7 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils.data_utils import rand_name
-from tempest import exceptions
+from tempest.common.utils import data_utils
 from tempest.test import attr
 
 
@@ -28,7 +27,8 @@
     def setUpClass(cls):
         super(RolesTestJSON, cls).setUpClass()
         for _ in xrange(5):
-            resp, role = cls.client.create_role(rand_name('role-'))
+            role_name = data_utils.rand_name(name='role-')
+            resp, role = cls.client.create_role(role_name)
             cls.data.roles.append(role)
 
     def _get_role_params(self):
@@ -55,23 +55,9 @@
         self.assertEqual(len(found), len(self.data.roles))
 
     @attr(type='gate')
-    def test_list_roles_by_unauthorized_user(self):
-        # Non-administrator user should not be able to list roles
-        self.assertRaises(exceptions.Unauthorized,
-                          self.non_admin_client.list_roles)
-
-    @attr(type='gate')
-    def test_list_roles_request_without_token(self):
-        # Request to list roles without a valid token should fail
-        token = self.client.get_auth()
-        self.client.delete_token(token)
-        self.assertRaises(exceptions.Unauthorized, self.client.list_roles)
-        self.client.clear_auth()
-
-    @attr(type='gate')
     def test_role_create_delete(self):
         # Role should be created, verified, and deleted
-        role_name = rand_name('role-test-')
+        role_name = data_utils.rand_name(name='role-test-')
         resp, body = self.client.create_role(role_name)
         self.assertIn('status', resp)
         self.assertTrue(resp['status'].startswith('2'))
@@ -90,23 +76,6 @@
         self.assertFalse(any(found))
 
     @attr(type='gate')
-    def test_role_create_blank_name(self):
-        # Should not be able to create a role with a blank name
-        self.assertRaises(exceptions.BadRequest, self.client.create_role, '')
-
-    @attr(type='gate')
-    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.assertIn('status', resp)
-        self.assertTrue(resp['status'].startswith('2'))
-        self.addCleanup(self.client.delete_role, role1_id)
-        self.assertRaises(exceptions.Conflict, self.client.create_role,
-                          role_name)
-
-    @attr(type='gate')
     def test_assign_user_role(self):
         # Assign a role to a user on a tenant
         (user, tenant, role) = self._get_role_params()
@@ -115,55 +84,6 @@
         self.assert_role_in_role_list(role, roles)
 
     @attr(type='gate')
-    def test_assign_user_role_by_unauthorized_user(self):
-        # Non-administrator user should not be authorized to
-        # assign a role to user
-        (user, tenant, role) = self._get_role_params()
-        self.assertRaises(exceptions.Unauthorized,
-                          self.non_admin_client.assign_user_role,
-                          tenant['id'], user['id'], role['id'])
-
-    @attr(type='gate')
-    def test_assign_user_role_request_without_token(self):
-        # Request to assign a role to a user without a valid token
-        (user, tenant, role) = self._get_role_params()
-        token = self.client.get_auth()
-        self.client.delete_token(token)
-        self.assertRaises(exceptions.Unauthorized,
-                          self.client.assign_user_role, tenant['id'],
-                          user['id'], role['id'])
-        self.client.clear_auth()
-
-    @attr(type='gate')
-    def test_assign_user_role_for_non_existent_user(self):
-        # Attempt to assign a role to a non existent user should fail
-        (user, tenant, role) = self._get_role_params()
-        self.assertRaises(exceptions.NotFound, self.client.assign_user_role,
-                          tenant['id'], 'junk-user-id-999', role['id'])
-
-    @attr(type='gate')
-    def test_assign_user_role_for_non_existent_role(self):
-        # Attempt to assign a non existent role to user should fail
-        (user, tenant, role) = self._get_role_params()
-        self.assertRaises(exceptions.NotFound, self.client.assign_user_role,
-                          tenant['id'], user['id'], 'junk-role-id-12345')
-
-    @attr(type='gate')
-    def test_assign_user_role_for_non_existent_tenant(self):
-        # Attempt to assign a role on a non existent tenant should fail
-        (user, tenant, role) = self._get_role_params()
-        self.assertRaises(exceptions.NotFound, self.client.assign_user_role,
-                          'junk-tenant-1234', user['id'], role['id'])
-
-    @attr(type='gate')
-    def test_assign_duplicate_user_role(self):
-        # Duplicate user role should not get assigned
-        (user, tenant, role) = self._get_role_params()
-        self.client.assign_user_role(tenant['id'], user['id'], role['id'])
-        self.assertRaises(exceptions.Conflict, self.client.assign_user_role,
-                          tenant['id'], user['id'], role['id'])
-
-    @attr(type='gate')
     def test_remove_user_role(self):
         # Remove a role assigned to a user on a tenant
         (user, tenant, role) = self._get_role_params()
@@ -174,62 +94,6 @@
         self.assertEqual(resp['status'], '204')
 
     @attr(type='gate')
-    def test_remove_user_role_by_unauthorized_user(self):
-        # Non-administrator user should not be authorized to
-        # remove a user's role
-        (user, tenant, role) = self._get_role_params()
-        resp, user_role = self.client.assign_user_role(tenant['id'],
-                                                       user['id'],
-                                                       role['id'])
-        self.assertRaises(exceptions.Unauthorized,
-                          self.non_admin_client.remove_user_role,
-                          tenant['id'], user['id'], role['id'])
-
-    @attr(type='gate')
-    def test_remove_user_role_request_without_token(self):
-        # Request to remove a user's role without a valid token
-        (user, tenant, role) = self._get_role_params()
-        resp, user_role = self.client.assign_user_role(tenant['id'],
-                                                       user['id'],
-                                                       role['id'])
-        token = self.client.get_auth()
-        self.client.delete_token(token)
-        self.assertRaises(exceptions.Unauthorized,
-                          self.client.remove_user_role, tenant['id'],
-                          user['id'], role['id'])
-        self.client.clear_auth()
-
-    @attr(type='gate')
-    def test_remove_user_role_non_existant_user(self):
-        # Attempt to remove a role from a non existent user should fail
-        (user, tenant, role) = self._get_role_params()
-        resp, user_role = self.client.assign_user_role(tenant['id'],
-                                                       user['id'],
-                                                       role['id'])
-        self.assertRaises(exceptions.NotFound, self.client.remove_user_role,
-                          tenant['id'], 'junk-user-id-123', role['id'])
-
-    @attr(type='gate')
-    def test_remove_user_role_non_existant_role(self):
-        # Attempt to delete a non existent role from a user should fail
-        (user, tenant, role) = self._get_role_params()
-        resp, user_role = self.client.assign_user_role(tenant['id'],
-                                                       user['id'],
-                                                       role['id'])
-        self.assertRaises(exceptions.NotFound, self.client.remove_user_role,
-                          tenant['id'], user['id'], 'junk-user-role-123')
-
-    @attr(type='gate')
-    def test_remove_user_role_non_existant_tenant(self):
-        # Attempt to remove a role from a non existent tenant should fail
-        (user, tenant, role) = self._get_role_params()
-        resp, user_role = self.client.assign_user_role(tenant['id'],
-                                                       user['id'],
-                                                       role['id'])
-        self.assertRaises(exceptions.NotFound, self.client.remove_user_role,
-                          'junk-tenant-id-123', user['id'], role['id'])
-
-    @attr(type='gate')
     def test_list_user_roles(self):
         # List roles assigned to a user on tenant
         (user, tenant, role) = self._get_role_params()
@@ -237,36 +101,6 @@
         resp, roles = self.client.list_user_roles(tenant['id'], user['id'])
         self.assert_role_in_role_list(role, roles)
 
-    @attr(type='gate')
-    def test_list_user_roles_by_unauthorized_user(self):
-        # Non-administrator user should not be authorized to list
-        # a user's roles
-        (user, tenant, role) = self._get_role_params()
-        self.client.assign_user_role(tenant['id'], user['id'], role['id'])
-        self.assertRaises(exceptions.Unauthorized,
-                          self.non_admin_client.list_user_roles, tenant['id'],
-                          user['id'])
-
-    @attr(type='gate')
-    def test_list_user_roles_request_without_token(self):
-        # Request to list user's roles without a valid token should fail
-        (user, tenant, role) = self._get_role_params()
-        token = self.client.get_auth()
-        self.client.delete_token(token)
-        try:
-            self.assertRaises(exceptions.Unauthorized,
-                              self.client.list_user_roles, tenant['id'],
-                              user['id'])
-        finally:
-            self.client.clear_auth()
-
-    @attr(type='gate')
-    def test_list_user_roles_for_non_existent_user(self):
-        # Attempt to list roles of a non existent user should fail
-        (user, tenant, role) = self._get_role_params()
-        self.assertRaises(exceptions.NotFound, self.client.list_user_roles,
-                          tenant['id'], 'junk-role-aabbcc11')
-
 
 class RolesTestXML(RolesTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/identity/admin/test_roles_negative.py b/tempest/api/identity/admin/test_roles_negative.py
new file mode 100644
index 0000000..83d1d4d
--- /dev/null
+++ b/tempest/api/identity/admin/test_roles_negative.py
@@ -0,0 +1,262 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD.
+# 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.identity import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class RolesNegativeTestJSON(base.BaseIdentityAdminTest):
+    _interface = 'json'
+
+    def _get_role_params(self):
+        self.data.setup_test_user()
+        self.data.setup_test_role()
+        user = self.get_user_by_name(self.data.test_user)
+        tenant = self.get_tenant_by_name(self.data.test_tenant)
+        role = self.get_role_by_name(self.data.test_role)
+        return (user, tenant, role)
+
+    @attr(type=['negative', 'gate'])
+    def test_list_roles_by_unauthorized_user(self):
+        # Non-administrator user should not be able to list roles
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.list_roles)
+
+    @attr(type=['negative', 'gate'])
+    def test_list_roles_request_without_token(self):
+        # Request to list roles without a valid token should fail
+        token = self.client.get_auth()
+        self.client.delete_token(token)
+        self.assertRaises(exceptions.Unauthorized, self.client.list_roles)
+        self.client.clear_auth()
+
+    @attr(type=['negative', 'gate'])
+    def test_role_create_blank_name(self):
+        # Should not be able to create a role with a blank name
+        self.assertRaises(exceptions.BadRequest, self.client.create_role, '')
+
+    @attr(type=['negative', 'gate'])
+    def test_create_role_by_unauthorized_user(self):
+        # Non-administrator user should not be able to create role
+        role_name = data_utils.rand_name(name='role-')
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.create_role, role_name)
+
+    @attr(type=['negative', 'gate'])
+    def test_create_role_request_without_token(self):
+        # Request to create role without a valid token should fail
+        token = self.client.get_auth()
+        self.client.delete_token(token)
+        role_name = data_utils.rand_name(name='role-')
+        self.assertRaises(exceptions.Unauthorized,
+                          self.client.create_role, role_name)
+        self.client.clear_auth()
+
+    @attr(type=['negative', 'gate'])
+    def test_role_create_duplicate(self):
+        # Role names should be unique
+        role_name = data_utils.rand_name(name='role-dup-')
+        resp, body = self.client.create_role(role_name)
+        role1_id = body.get('id')
+        self.assertIn('status', resp)
+        self.assertTrue(resp['status'].startswith('2'))
+        self.addCleanup(self.client.delete_role, role1_id)
+        self.assertRaises(exceptions.Conflict, self.client.create_role,
+                          role_name)
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_role_by_unauthorized_user(self):
+        # Non-administrator user should not be able to delete role
+        role_name = data_utils.rand_name(name='role-')
+        resp, body = self.client.create_role(role_name)
+        self.assertEqual(200, resp.status)
+        self.data.roles.append(body)
+        role_id = body.get('id')
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.delete_role, role_id)
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_role_request_without_token(self):
+        # Request to delete role without a valid token should fail
+        role_name = data_utils.rand_name(name='role-')
+        resp, body = self.client.create_role(role_name)
+        self.assertEqual(200, resp.status)
+        self.data.roles.append(body)
+        role_id = body.get('id')
+        token = self.client.get_auth()
+        self.client.delete_token(token)
+        self.assertRaises(exceptions.Unauthorized,
+                          self.client.delete_role,
+                          role_id)
+        self.client.clear_auth()
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_role_non_existent(self):
+        # Attempt to delete a non existent role should fail
+        non_existent_role = str(uuid.uuid4().hex)
+        self.assertRaises(exceptions.NotFound, self.client.delete_role,
+                          non_existent_role)
+
+    @attr(type=['negative', 'gate'])
+    def test_assign_user_role_by_unauthorized_user(self):
+        # Non-administrator user should not be authorized to
+        # assign a role to user
+        (user, tenant, role) = self._get_role_params()
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.assign_user_role,
+                          tenant['id'], user['id'], role['id'])
+
+    @attr(type=['negative', 'gate'])
+    def test_assign_user_role_request_without_token(self):
+        # Request to assign a role to a user without a valid token
+        (user, tenant, role) = self._get_role_params()
+        token = self.client.get_auth()
+        self.client.delete_token(token)
+        self.assertRaises(exceptions.Unauthorized,
+                          self.client.assign_user_role, tenant['id'],
+                          user['id'], role['id'])
+        self.client.clear_auth()
+
+    @attr(type=['negative', 'gate'])
+    def test_assign_user_role_for_non_existent_user(self):
+        # Attempt to assign a role to a non existent user should fail
+        (user, tenant, role) = self._get_role_params()
+        non_existent_user = str(uuid.uuid4().hex)
+        self.assertRaises(exceptions.NotFound, self.client.assign_user_role,
+                          tenant['id'], non_existent_user, role['id'])
+
+    @attr(type=['negative', 'gate'])
+    def test_assign_user_role_for_non_existent_role(self):
+        # Attempt to assign a non existent role to user should fail
+        (user, tenant, role) = self._get_role_params()
+        non_existent_role = str(uuid.uuid4().hex)
+        self.assertRaises(exceptions.NotFound, self.client.assign_user_role,
+                          tenant['id'], user['id'], non_existent_role)
+
+    @attr(type=['negative', 'gate'])
+    def test_assign_user_role_for_non_existent_tenant(self):
+        # Attempt to assign a role on a non existent tenant should fail
+        (user, tenant, role) = self._get_role_params()
+        non_existent_tenant = str(uuid.uuid4().hex)
+        self.assertRaises(exceptions.NotFound, self.client.assign_user_role,
+                          non_existent_tenant, user['id'], role['id'])
+
+    @attr(type=['negative', 'gate'])
+    def test_assign_duplicate_user_role(self):
+        # Duplicate user role should not get assigned
+        (user, tenant, role) = self._get_role_params()
+        self.client.assign_user_role(tenant['id'], user['id'], role['id'])
+        self.assertRaises(exceptions.Conflict, self.client.assign_user_role,
+                          tenant['id'], user['id'], role['id'])
+
+    @attr(type=['negative', 'gate'])
+    def test_remove_user_role_by_unauthorized_user(self):
+        # Non-administrator user should not be authorized to
+        # remove a user's role
+        (user, tenant, role) = self._get_role_params()
+        resp, user_role = self.client.assign_user_role(tenant['id'],
+                                                       user['id'],
+                                                       role['id'])
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.remove_user_role,
+                          tenant['id'], user['id'], role['id'])
+
+    @attr(type=['negative', 'gate'])
+    def test_remove_user_role_request_without_token(self):
+        # Request to remove a user's role without a valid token
+        (user, tenant, role) = self._get_role_params()
+        resp, user_role = self.client.assign_user_role(tenant['id'],
+                                                       user['id'],
+                                                       role['id'])
+        token = self.client.get_auth()
+        self.client.delete_token(token)
+        self.assertRaises(exceptions.Unauthorized,
+                          self.client.remove_user_role, tenant['id'],
+                          user['id'], role['id'])
+        self.client.clear_auth()
+
+    @attr(type=['negative', 'gate'])
+    def test_remove_user_role_non_existant_user(self):
+        # Attempt to remove a role from a non existent user should fail
+        (user, tenant, role) = self._get_role_params()
+        resp, user_role = self.client.assign_user_role(tenant['id'],
+                                                       user['id'],
+                                                       role['id'])
+        non_existant_user = str(uuid.uuid4().hex)
+        self.assertRaises(exceptions.NotFound, self.client.remove_user_role,
+                          tenant['id'], non_existant_user, role['id'])
+
+    @attr(type=['negative', 'gate'])
+    def test_remove_user_role_non_existant_role(self):
+        # Attempt to delete a non existent role from a user should fail
+        (user, tenant, role) = self._get_role_params()
+        resp, user_role = self.client.assign_user_role(tenant['id'],
+                                                       user['id'],
+                                                       role['id'])
+        non_existant_role = str(uuid.uuid4().hex)
+        self.assertRaises(exceptions.NotFound, self.client.remove_user_role,
+                          tenant['id'], user['id'], non_existant_role)
+
+    @attr(type=['negative', 'gate'])
+    def test_remove_user_role_non_existant_tenant(self):
+        # Attempt to remove a role from a non existent tenant should fail
+        (user, tenant, role) = self._get_role_params()
+        resp, user_role = self.client.assign_user_role(tenant['id'],
+                                                       user['id'],
+                                                       role['id'])
+        non_existant_tenant = str(uuid.uuid4().hex)
+        self.assertRaises(exceptions.NotFound, self.client.remove_user_role,
+                          non_existant_tenant, user['id'], role['id'])
+
+    @attr(type=['negative', 'gate'])
+    def test_list_user_roles_by_unauthorized_user(self):
+        # Non-administrator user should not be authorized to list
+        # a user's roles
+        (user, tenant, role) = self._get_role_params()
+        self.client.assign_user_role(tenant['id'], user['id'], role['id'])
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.list_user_roles, tenant['id'],
+                          user['id'])
+
+    @attr(type=['negative', 'gate'])
+    def test_list_user_roles_request_without_token(self):
+        # Request to list user's roles without a valid token should fail
+        (user, tenant, role) = self._get_role_params()
+        token = self.client.get_auth()
+        self.client.delete_token(token)
+        try:
+            self.assertRaises(exceptions.Unauthorized,
+                              self.client.list_user_roles, tenant['id'],
+                              user['id'])
+        finally:
+            self.client.clear_auth()
+
+    @attr(type=['negative', 'gate'])
+    def test_list_user_roles_for_non_existent_user(self):
+        # Attempt to list roles of a non existent user should fail
+        (user, tenant, role) = self._get_role_params()
+        non_existent_user = str(uuid.uuid4().hex)
+        self.assertRaises(exceptions.NotFound, self.client.list_user_roles,
+                          tenant['id'], non_existent_user)
+
+
+class RolesTestXML(RolesNegativeTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/identity/admin/test_users.py b/tempest/api/identity/admin/test_users.py
index 906fad3..5d5a814 100644
--- a/tempest/api/identity/admin/test_users.py
+++ b/tempest/api/identity/admin/test_users.py
@@ -31,8 +31,6 @@
         cls.alt_user = data_utils.rand_name('test_user_')
         cls.alt_password = data_utils.rand_name('pass_')
         cls.alt_email = cls.alt_user + '@testmail.tm'
-        cls.alt_tenant = data_utils.rand_name('test_tenant_')
-        cls.alt_description = data_utils.rand_name('desc_')
 
     @attr(type='smoke')
     def test_create_user(self):
diff --git a/tempest/api/identity/admin/test_users_negative.py b/tempest/api/identity/admin/test_users_negative.py
index b29d155..e3e5a63 100644
--- a/tempest/api/identity/admin/test_users_negative.py
+++ b/tempest/api/identity/admin/test_users_negative.py
@@ -31,8 +31,6 @@
         cls.alt_user = rand_name('test_user_')
         cls.alt_password = rand_name('pass_')
         cls.alt_email = cls.alt_user + '@testmail.tm'
-        cls.alt_tenant = rand_name('test_tenant_')
-        cls.alt_description = rand_name('desc_')
 
     @attr(type=['negative', 'gate'])
     def test_create_user_by_unauthorized_user(self):
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index fcd895e..28ed5b6 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -18,7 +18,7 @@
 
 from tempest import clients
 from tempest.common import isolated_creds
-from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils import data_utils
 from tempest import exceptions
 from tempest.openstack.common import log as logging
 import tempest.test
@@ -63,7 +63,7 @@
     @classmethod
     def create_image(cls, **kwargs):
         """Wrapper that returns a test image."""
-        name = rand_name(cls.__name__ + "-instance")
+        name = data_utils.rand_name(cls.__name__ + "-instance")
 
         if 'name' in kwargs:
             name = kwargs.pop('name')
@@ -127,3 +127,40 @@
         if not cls.config.image_feature_enabled.api_v2:
             msg = "Glance API v2 not supported"
             raise cls.skipException(msg)
+
+
+class BaseV2MemeberImageTest(BaseImageTest):
+
+    @classmethod
+    def setUpClass(cls):
+        super(BaseV2MemeberImageTest, cls).setUpClass()
+        if cls.config.compute.allow_tenant_isolation:
+            creds = cls.isolated_creds.get_alt_creds()
+            username, tenant_name, password = creds
+            cls.os_alt = clients.Manager(username=username,
+                                         password=password,
+                                         tenant_name=tenant_name,
+                                         interface=cls._interface)
+            cls.alt_tenant_id = cls.isolated_creds.get_alt_tenant()['id']
+        else:
+            cls.os_alt = clients.AltManager()
+            alt_tenant_name = cls.os_alt.tenant_name
+            identity_client = cls._get_identity_admin_client()
+            cls.alt_tenant_id = identity_client.get_tenant_by_name(
+                alt_tenant_name)['id']
+        cls.os_img_client = cls.os.image_client_v2
+        cls.alt_img_client = cls.os_alt.image_client_v2
+
+    def _list_image_ids_as_alt(self):
+        _, image_list = self.alt_img_client.image_list()
+        image_ids = map(lambda x: x['id'], image_list)
+        return image_ids
+
+    def _create_image(self):
+        name = data_utils.rand_name('image')
+        resp, image = self.os_img_client.create_image(name,
+                                                      container_format='bare',
+                                                      disk_format='raw')
+        image_id = image['id']
+        self.addCleanup(self.os_img_client.delete_image, image_id)
+        return image_id
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
new file mode 100644
index 0000000..954c79d
--- /dev/null
+++ b/tempest/api/image/v2/test_images_member.py
@@ -0,0 +1,55 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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.
+
+from tempest.api.image import base
+from tempest.test import attr
+
+
+class ImagesMemberTest(base.BaseV2MemeberImageTest):
+    _interface = 'json'
+
+    @attr(type='gate')
+    def test_image_share_accept(self):
+        image_id = self._create_image()
+        resp, member = self.os_img_client.add_member(image_id,
+                                                     self.alt_tenant_id)
+        self.assertEqual(member['member_id'], self.alt_tenant_id)
+        self.assertEqual(member['image_id'], image_id)
+        self.assertEqual(member['status'], 'pending')
+        self.assertNotIn(image_id, self._list_image_ids_as_alt())
+        self.alt_img_client.update_member_status(image_id,
+                                                 self.alt_tenant_id,
+                                                 'accepted')
+        self.assertIn(image_id, self._list_image_ids_as_alt())
+        _, body = self.os_img_client.get_image_membership(image_id)
+        members = body['members']
+        member = members[0]
+        self.assertEqual(len(members), 1, str(members))
+        self.assertEqual(member['member_id'], self.alt_tenant_id)
+        self.assertEqual(member['image_id'], image_id)
+        self.assertEqual(member['status'], 'accepted')
+
+    @attr(type='gate')
+    def test_image_share_reject(self):
+        image_id = self._create_image()
+        resp, member = self.os_img_client.add_member(image_id,
+                                                     self.alt_tenant_id)
+        self.assertEqual(member['member_id'], self.alt_tenant_id)
+        self.assertEqual(member['image_id'], image_id)
+        self.assertEqual(member['status'], 'pending')
+        self.assertNotIn(image_id, self._list_image_ids_as_alt())
+        self.alt_img_client.update_member_status(image_id,
+                                                 self.alt_tenant_id,
+                                                 'rejected')
+        self.assertNotIn(image_id, self._list_image_ids_as_alt())
diff --git a/tempest/api/image/v2/test_images_member_negative.py b/tempest/api/image/v2/test_images_member_negative.py
new file mode 100644
index 0000000..3c17959
--- /dev/null
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -0,0 +1,43 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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.
+
+from tempest.api.image import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class ImagesMemberNegativeTest(base.BaseV2MemeberImageTest):
+    _interface = 'json'
+
+    @attr(type=['negative', 'gate'])
+    def test_image_share_invalid_status(self):
+        image_id = self._create_image()
+        resp, member = self.os_img_client.add_member(image_id,
+                                                     self.alt_tenant_id)
+        self.assertEqual(member['status'], 'pending')
+        self.assertRaises(exceptions.BadRequest,
+                          self.alt_img_client.update_member_status,
+                          image_id, self.alt_tenant_id, 'notavalidstatus')
+
+    @attr(type=['negative', 'gate'])
+    def test_image_share_owner_cannot_accept(self):
+        image_id = self._create_image()
+        resp, member = self.os_img_client.add_member(image_id,
+                                                     self.alt_tenant_id)
+        self.assertEqual(member['status'], 'pending')
+        self.assertNotIn(image_id, self._list_image_ids_as_alt())
+        self.assertRaises(exceptions.Unauthorized,
+                          self.os_img_client.update_member_status,
+                          image_id, self.alt_tenant_id, 'accepted')
+        self.assertNotIn(image_id, self._list_image_ids_as_alt())
diff --git a/tempest/api/network/admin/__init__.py b/tempest/api/network/admin/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/network/admin/__init__.py
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
new file mode 100644
index 0000000..5b04cbb
--- /dev/null
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -0,0 +1,64 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+#
+#    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.
+
+from tempest.api.network import base
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+class L3AgentSchedulerJSON(base.BaseAdminNetworkTest):
+    _interface = 'json'
+
+    """
+    Tests the following operations in the Neutron API using the REST client for
+    Neutron:
+
+        List routers that the given L3 agent is hosting.
+        List L3 agents hosting the given router.
+
+    v2.0 of the Neutron API is assumed. It is also assumed that the following
+    options are defined in the [network] section of etc/tempest.conf:
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        super(L3AgentSchedulerJSON, cls).setUpClass()
+
+    @attr(type='smoke')
+    def test_list_routers_on_l3_agent(self):
+        resp, body = self.admin_client.list_agents()
+        agents = body['agents']
+        for a in agents:
+            if a['agent_type'] == 'L3 agent':
+                agent = a
+        resp, body = self.admin_client.list_routers_on_l3_agent(
+            agent['id'])
+        self.assertEqual('200', resp['status'])
+
+    @attr(type='smoke')
+    def test_list_l3_agents_hosting_router(self):
+        name = rand_name('router-')
+        resp, router = self.client.create_router(name)
+        self.assertEqual('201', resp['status'])
+        resp, body = self.admin_client.list_l3_agents_hosting_router(
+            router['router']['id'])
+        self.assertEqual('200', resp['status'])
+        resp, _ = self.client.delete_router(router['router']['id'])
+        self.assertEqual(204, resp.status)
+
+
+class L3AgentSchedulerXML(L3AgentSchedulerJSON):
+    _interface = 'xml'
diff --git a/tempest/api/network/test_service_type_management.py b/tempest/api/network/test_service_type_management.py
new file mode 100644
index 0000000..ae03e96
--- /dev/null
+++ b/tempest/api/network/test_service_type_management.py
@@ -0,0 +1,30 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+#    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.
+
+from tempest.api.network import base
+from tempest.test import attr
+
+
+class ServiceTypeManagementTestJSON(base.BaseNetworkTest):
+    _interface = 'json'
+
+    @attr(type='smoke')
+    def test_service_provider_list(self):
+        resp, body = self.client.list_service_providers()
+        self.assertEqual(resp['status'], '200')
+        self.assertIsInstance(body['service_providers'], list)
+
+
+class ServiceTypeManagementTestXML(ServiceTypeManagementTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 538d5be..f42c2f5 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -237,6 +237,36 @@
         resp, body = self.client.unreserve_volume(self.volume['id'])
         self.assertEqual(202, resp.status)
 
+    @attr(type=['negative', 'gate'])
+    def test_list_volumes_with_nonexistent_name(self):
+        v_name = rand_name('Volume-')
+        params = {'display_name': v_name}
+        resp, fetched_volume = self.client.list_volumes(params)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(0, len(fetched_volume))
+
+    @attr(type=['negative', 'gate'])
+    def test_list_volumes_detail_with_nonexistent_name(self):
+        v_name = rand_name('Volume-')
+        params = {'display_name': v_name}
+        resp, fetched_volume = self.client.list_volumes_with_detail(params)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(0, len(fetched_volume))
+
+    @attr(type=['negative', 'gate'])
+    def test_list_volumes_with_invalid_status(self):
+        params = {'status': 'null'}
+        resp, fetched_volume = self.client.list_volumes(params)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(0, len(fetched_volume))
+
+    @attr(type=['negative', 'gate'])
+    def test_list_volumes_detail_with_invalid_status(self):
+        params = {'status': 'null'}
+        resp, fetched_volume = self.client.list_volumes_with_detail(params)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(0, len(fetched_volume))
+
 
 class VolumesNegativeTestXML(VolumesNegativeTest):
     _interface = 'xml'
diff --git a/tempest/clients.py b/tempest/clients.py
index 610a9aa..a52ed5d 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -23,6 +23,8 @@
     AggregatesClientJSON
 from tempest.services.compute.json.availability_zone_client import \
     AvailabilityZoneClientJSON
+from tempest.services.compute.json.certificates_client import \
+    CertificatesClientJSON
 from tempest.services.compute.json.extensions_client import \
     ExtensionsClientJSON
 from tempest.services.compute.json.fixed_ips_client import FixedIPsClientJSON
@@ -46,16 +48,22 @@
     TenantUsagesClientJSON
 from tempest.services.compute.json.volumes_extensions_client import \
     VolumesExtensionsClientJSON
+from tempest.services.compute.v3.json.extensions_client import \
+    ExtensionsV3ClientJSON
 from tempest.services.compute.v3.json.servers_client import \
     ServersV3ClientJSON
 from tempest.services.compute.v3.json.services_client import \
     ServicesV3ClientJSON
+from tempest.services.compute.v3.xml.extensions_client import \
+    ExtensionsV3ClientXML
 from tempest.services.compute.v3.xml.servers_client import ServersV3ClientXML
 from tempest.services.compute.v3.xml.services_client import \
     ServicesV3ClientXML
 from tempest.services.compute.xml.aggregates_client import AggregatesClientXML
 from tempest.services.compute.xml.availability_zone_client import \
     AvailabilityZoneClientXML
+from tempest.services.compute.xml.certificates_client import \
+    CertificatesClientXML
 from tempest.services.compute.xml.extensions_client import ExtensionsClientXML
 from tempest.services.compute.xml.fixed_ips_client import FixedIPsClientXML
 from tempest.services.compute.xml.flavors_client import FlavorsClientXML
@@ -173,6 +181,7 @@
         self.servers_client_v3_auth = None
 
         if interface == 'xml':
+            self.certificates_client = CertificatesClientXML(*client_args)
             self.servers_client = ServersClientXML(*client_args)
             self.servers_v3_client = ServersV3ClientXML(*client_args)
             self.limits_client = LimitsClientXML(*client_args)
@@ -180,6 +189,7 @@
             self.keypairs_client = KeyPairsClientXML(*client_args)
             self.quotas_client = QuotasClientXML(*client_args)
             self.flavors_client = FlavorsClientXML(*client_args)
+            self.extensions_v3_client = ExtensionsV3ClientXML(*client_args)
             self.extensions_client = ExtensionsClientXML(*client_args)
             self.volumes_extensions_client = VolumesExtensionsClientXML(
                 *client_args)
@@ -213,6 +223,7 @@
                     *client_args_v3_auth)
 
         elif interface == 'json':
+            self.certificates_client = CertificatesClientJSON(*client_args)
             self.servers_client = ServersClientJSON(*client_args)
             self.servers_v3_client = ServersV3ClientJSON(*client_args)
             self.limits_client = LimitsClientJSON(*client_args)
@@ -220,6 +231,7 @@
             self.keypairs_client = KeyPairsClientJSON(*client_args)
             self.quotas_client = QuotasClientJSON(*client_args)
             self.flavors_client = FlavorsClientJSON(*client_args)
+            self.extensions_v3_client = ExtensionsV3ClientJSON(*client_args)
             self.extensions_client = ExtensionsClientJSON(*client_args)
             self.volumes_extensions_client = VolumesExtensionsClientJSON(
                 *client_args)
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 15569cd..bea2cdc 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -24,7 +24,8 @@
 
 
 # NOTE(afazekas): This function needs to know a token and a subject.
-def wait_for_server_status(client, server_id, status, ready_wait=True):
+def wait_for_server_status(client, server_id, status, ready_wait=True,
+                           extra_timeout=0):
     """Waits for a server to reach a given status."""
 
     def _get_task_state(body):
@@ -37,6 +38,7 @@
     old_status = server_status = body['status']
     old_task_state = task_state = _get_task_state(body)
     start_time = int(time.time())
+    timeout = client.build_timeout + extra_timeout
     while True:
         # NOTE(afazekas): Now the BUILD status only reached
         # between the UNKOWN->ACTIVE transition.
@@ -70,12 +72,12 @@
         if server_status == 'ERROR':
             raise exceptions.BuildErrorException(server_id=server_id)
 
-        timed_out = int(time.time()) - start_time >= client.build_timeout
+        timed_out = int(time.time()) - start_time >= timeout
 
         if timed_out:
             message = ('Server %s failed to reach %s status within the '
                        'required time (%s s).' %
-                       (server_id, status, client.build_timeout))
+                       (server_id, status, timeout))
             message += ' Current status: %s.' % server_status
             raise exceptions.TimeoutException(message)
         old_status = server_status
diff --git a/tempest/config.py b/tempest/config.py
index 76461fb..ff8a170 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -196,7 +196,14 @@
     cfg.StrOpt('volume_device_name',
                default='vdb',
                help="Expected device name when a volume is attached to "
-                    "an instance")
+                    "an instance"),
+    cfg.IntOpt('shelved_offload_time',
+               default=0,
+               help='Time in seconds before a shelved instance is eligible '
+                    'for removing from a host.  -1 never offload, 0 offload '
+                    'when shelved. This time should be the same as the time '
+                    'of nova.conf, and some tests will run for as long as the '
+                    'time.')
 ]
 
 compute_features_group = cfg.OptGroup(name='compute-feature-enabled',
diff --git a/tempest/services/compute/json/certificates_client.py b/tempest/services/compute/json/certificates_client.py
new file mode 100644
index 0000000..9fdce17
--- /dev/null
+++ b/tempest/services/compute/json/certificates_client.py
@@ -0,0 +1,42 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp
+# 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 json
+
+from tempest.common.rest_client import RestClient
+
+
+class CertificatesClientJSON(RestClient):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(CertificatesClientJSON, self).__init__(config, username,
+                                                     password,
+                                                     auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def get_certificate(self, id):
+        url = "os-certificates/%s" % (id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['certificate']
+
+    def create_certificate(self):
+        """create certificates."""
+        url = "os-certificates"
+        resp, body = self.post(url, None, self.headers)
+        body = json.loads(body)
+        return resp, body['certificate']
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index f61a8d4..3c6a40f 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -154,9 +154,10 @@
         body = json.loads(body)
         return resp, body
 
-    def wait_for_server_status(self, server_id, status):
+    def wait_for_server_status(self, server_id, status, extra_timeout=0):
         """Waits for a server to reach a given status."""
-        return waiters.wait_for_server_status(self, server_id, status)
+        return waiters.wait_for_server_status(self, server_id, status,
+                                              extra_timeout=extra_timeout)
 
     def wait_for_server_termination(self, server_id, ignore_error=False):
         """Waits for server to reach termination."""
@@ -197,6 +198,13 @@
             body = json.loads(body)[response_key]
         return resp, body
 
+    def create_backup(self, server_id, backup_type, rotation, name):
+        """Backup a server instance."""
+        return self.action(server_id, "createBackup", None,
+                           backup_type=backup_type,
+                           rotation=rotation,
+                           name=name)
+
     def change_password(self, server_id, adminPass):
         """Changes the root password for the server."""
         return self.action(server_id, 'changePassword', None,
@@ -348,25 +356,33 @@
         return self.action(server_id, 'unlock', None, **kwargs)
 
     def suspend_server(self, server_id, **kwargs):
-        """Suspends the provded server."""
+        """Suspends the provided server."""
         return self.action(server_id, 'suspend', None, **kwargs)
 
     def resume_server(self, server_id, **kwargs):
-        """Un-suspends the provded server."""
+        """Un-suspends the provided server."""
         return self.action(server_id, 'resume', None, **kwargs)
 
     def pause_server(self, server_id, **kwargs):
-        """Pauses the provded server."""
+        """Pauses the provided server."""
         return self.action(server_id, 'pause', None, **kwargs)
 
     def unpause_server(self, server_id, **kwargs):
-        """Un-pauses the provded server."""
+        """Un-pauses the provided server."""
         return self.action(server_id, 'unpause', None, **kwargs)
 
     def reset_state(self, server_id, state='error'):
         """Resets the state of a server to active/error."""
         return self.action(server_id, 'os-resetState', None, state=state)
 
+    def shelve_server(self, server_id, **kwargs):
+        """Shelves the provided server."""
+        return self.action(server_id, 'shelve', None, **kwargs)
+
+    def unshelve_server(self, server_id, **kwargs):
+        """Un-shelves the provided server."""
+        return self.action(server_id, 'unshelve', None, **kwargs)
+
     def get_console_output(self, server_id, length):
         return self.action(server_id, 'os-getConsoleOutput', 'output',
                            length=length)
diff --git a/tempest/services/compute/v3/json/extensions_client.py b/tempest/services/compute/v3/json/extensions_client.py
index ce46a9b..60c0217 100644
--- a/tempest/services/compute/v3/json/extensions_client.py
+++ b/tempest/services/compute/v3/json/extensions_client.py
@@ -20,12 +20,13 @@
 from tempest.common.rest_client import RestClient
 
 
-class ExtensionsClientJSON(RestClient):
+class ExtensionsV3ClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(ExtensionsClientJSON, self).__init__(config, username, password,
-                                                   auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
+        super(ExtensionsV3ClientJSON, self).__init__(config, username,
+                                                     password, auth_url,
+                                                     tenant_name)
+        self.service = self.config.compute.catalog_v3_type
 
     def list_extensions(self):
         url = 'extensions'
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index a005edb..cddbb53 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -42,7 +42,7 @@
         image_ref (Required): Reference to the image used to build the server.
         flavor_ref (Required): The flavor used to build the server.
         Following optional keyword arguments are accepted:
-        admin_pass: Sets the initial root password.
+        admin_password: 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
@@ -64,7 +64,7 @@
             'flavor_ref': flavor_ref
         }
 
-        for option in ['personality', 'admin_pass', 'key_name',
+        for option in ['personality', 'admin_password', 'key_name',
                        'security_groups', 'networks',
                        ('os-user-data:user_data', 'user_data'),
                        ('os-availability-zone:availability_zone',
@@ -161,9 +161,10 @@
         body = json.loads(body)
         return resp, body
 
-    def wait_for_server_status(self, server_id, status):
+    def wait_for_server_status(self, server_id, status, extra_timeout=0):
         """Waits for a server to reach a given status."""
-        return waiters.wait_for_server_status(self, server_id, status)
+        return waiters.wait_for_server_status(self, server_id, status,
+                                              extra_timeout=extra_timeout)
 
     def wait_for_server_termination(self, server_id, ignore_error=False):
         """Waits for server to reach termination."""
@@ -367,9 +368,10 @@
         return self.action(server_id, 'get_console_output', 'output',
                            length=length)
 
-    def rescue_server(self, server_id, adminPass=None):
+    def rescue_server(self, server_id, admin_password=None):
         """Rescue the provided server."""
-        return self.action(server_id, 'rescue', None, admin_pass=adminPass)
+        return self.action(server_id, 'rescue', None,
+                           admin_password=admin_password)
 
     def unrescue_server(self, server_id):
         """Unrescue the provided server."""
diff --git a/tempest/services/compute/v3/xml/extensions_client.py b/tempest/services/compute/v3/xml/extensions_client.py
index 1395b5a..e03251c 100644
--- a/tempest/services/compute/v3/xml/extensions_client.py
+++ b/tempest/services/compute/v3/xml/extensions_client.py
@@ -20,12 +20,12 @@
 from tempest.services.compute.xml.common import xml_to_json
 
 
-class ExtensionsClientXML(RestClientXML):
+class ExtensionsV3ClientXML(RestClientXML):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(ExtensionsClientXML, self).__init__(config, username, password,
-                                                  auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
+        super(ExtensionsV3ClientXML, self).__init__(config, username, password,
+                                                    auth_url, tenant_name)
+        self.service = self.config.compute.catalog_v3_type
 
     def _parse_array(self, node):
         array = []
diff --git a/tempest/services/compute/v3/xml/servers_client.py b/tempest/services/compute/v3/xml/servers_client.py
index 6f38b6a..2ad5849 100644
--- a/tempest/services/compute/v3/xml/servers_client.py
+++ b/tempest/services/compute/v3/xml/servers_client.py
@@ -295,7 +295,7 @@
                          flavor_ref=flavor_ref,
                          image_ref=image_ref,
                          name=name)
-        attrs = ["admin_pass", "access_ip_v4", "access_ip_v6", "key_name",
+        attrs = ["admin_password", "access_ip_v4", "access_ip_v6", "key_name",
                  ("os-user-data:user_data",
                   'user_data',
                   'xmlns:os-user-data',
@@ -376,9 +376,10 @@
         server = self._parse_server(etree.fromstring(body))
         return resp, server
 
-    def wait_for_server_status(self, server_id, status):
+    def wait_for_server_status(self, server_id, status, extra_timeout=0):
         """Waits for a server to reach a given status."""
-        return waiters.wait_for_server_status(self, server_id, status)
+        return waiters.wait_for_server_status(self, server_id, status,
+                                              extra_timeout=extra_timeout)
 
     def wait_for_server_termination(self, server_id, ignore_error=False):
         """Waits for server to reach termination."""
@@ -438,7 +439,7 @@
 
     def change_password(self, server_id, password):
         return self.action(server_id, "change_password", None,
-                           admin_pass=password)
+                           admin_password=password)
 
     def reboot(self, server_id, reboot_type):
         return self.action(server_id, "reboot", None, type=reboot_type)
@@ -586,9 +587,10 @@
         return self.action(server_id, 'get_console_output', 'output',
                            length=length)
 
-    def rescue_server(self, server_id, admin_pass=None):
+    def rescue_server(self, server_id, admin_password=None):
         """Rescue the provided server."""
-        return self.action(server_id, 'rescue', None, admin_pass=admin_pass)
+        return self.action(server_id, 'rescue', None,
+                           admin_password=admin_password)
 
     def unrescue_server(self, server_id):
         """Unrescue the provided server."""
diff --git a/tempest/services/compute/xml/certificates_client.py b/tempest/services/compute/xml/certificates_client.py
new file mode 100644
index 0000000..7523352
--- /dev/null
+++ b/tempest/services/compute/xml/certificates_client.py
@@ -0,0 +1,40 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp
+# 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.
+
+
+from tempest.common.rest_client import RestClientXML
+
+
+class CertificatesClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(CertificatesClientXML, self).__init__(config, username, password,
+                                                    auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def get_certificate(self, id):
+        url = "os-certificates/%s" % (id)
+        resp, body = self.get(url, self.headers)
+        body = self._parse_resp(body)
+        return resp, body
+
+    def create_certificate(self):
+        """create certificates."""
+        url = "os-certificates"
+        resp, body = self.post(url, None, self.headers)
+        body = self._parse_resp(body)
+        return resp, body
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 48ca71a..3e5413c 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -217,6 +217,14 @@
         """Un-pauses the provided server."""
         return self.action(server_id, 'unpause', None, **kwargs)
 
+    def shelve_server(self, server_id, **kwargs):
+        """Shelves the provided server."""
+        return self.action(server_id, 'shelve', None, **kwargs)
+
+    def unshelve_server(self, server_id, **kwargs):
+        """Un-shelves the provided server."""
+        return self.action(server_id, 'unshelve', None, **kwargs)
+
     def reset_state(self, server_id, state='error'):
         """Resets the state of a server to active/error."""
         return self.action(server_id, 'os-resetState', None, state=state)
@@ -351,9 +359,10 @@
         server = self._parse_server(etree.fromstring(body))
         return resp, server
 
-    def wait_for_server_status(self, server_id, status):
+    def wait_for_server_status(self, server_id, status, extra_timeout=0):
         """Waits for a server to reach a given status."""
-        return waiters.wait_for_server_status(self, server_id, status)
+        return waiters.wait_for_server_status(self, server_id, status,
+                                              extra_timeout=extra_timeout)
 
     def wait_for_server_termination(self, server_id, ignore_error=False):
         """Waits for server to reach termination."""
@@ -411,6 +420,13 @@
             body = xml_to_json(etree.fromstring(body))
         return resp, body
 
+    def create_backup(self, server_id, backup_type, rotation, name):
+        """Backup a server instance."""
+        return self.action(server_id, "createBackup", None,
+                           backup_type=backup_type,
+                           rotation=rotation,
+                           name=name)
+
     def change_password(self, server_id, password):
         return self.action(server_id, "changePassword", None,
                            adminPass=password)
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 0a7f0e2..9f5a405 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -187,9 +187,15 @@
         body = json.loads(body)
         return resp, body['images']
 
-    def image_list_detail(self, **kwargs):
+    def image_list_detail(self, properties=dict(), **kwargs):
         url = 'v1/images/detail'
 
+        params = {}
+        for key, value in properties.items():
+            params['property-%s' % key] = value
+
+        kwargs.update(params)
+
         if len(kwargs) > 0:
             url += '?%s' % urllib.urlencode(kwargs)
 
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index 342a09c..3d37267 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -134,3 +134,27 @@
         url = 'v2/images/%s/tags/%s' % (image_id, tag)
         resp, _ = self.delete(url)
         return resp
+
+    def get_image_membership(self, image_id):
+        url = 'v2/images/%s/members' % image_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp)
+        return resp, body
+
+    def add_member(self, image_id, member_id):
+        url = 'v2/images/%s/members' % image_id
+        data = json.dumps({'member': member_id})
+        resp, body = self.post(url, data, self.headers)
+        body = json.loads(body)
+        self.expected_success(200, resp)
+        return resp, body
+
+    def update_member_status(self, image_id, member_id, status):
+        """Valid status are: ``pending``, ``accepted``,  ``rejected``."""
+        url = 'v2/images/%s/members/%s' % (image_id, member_id)
+        data = json.dumps({'status': status})
+        resp, body = self.put(url, data, self.headers)
+        body = json.loads(body)
+        self.expected_success(200, resp)
+        return resp, body
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index e7cd33f..aa7f2f2 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -673,3 +673,27 @@
         resp, body = self.get(uri, self.headers)
         body = json.loads(body)
         return resp, body
+
+    def list_agents(self):
+        uri = '%s/agents' % self.uri_prefix
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def list_routers_on_l3_agent(self, agent_id):
+        uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def list_l3_agents_hosting_router(self, router_id):
+        uri = '%s/routers/%s/l3-agents' % (self.uri_prefix, router_id)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def list_service_providers(self):
+        uri = '%s/service-providers' % self.uri_prefix
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index 04ad86f..5af2dfb 100755
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -554,6 +554,32 @@
         ports = {"ports": ports}
         return resp, ports
 
+    def list_agents(self):
+        uri = '%s/agents' % self.uri_prefix
+        resp, body = self.get(uri, self.headers)
+        agents = self._parse_array(etree.fromstring(body))
+        agents = {'agents': agents}
+        return resp, agents
+
+    def list_routers_on_l3_agent(self, agent_id):
+        uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
+        resp, body = self.get(uri, self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def list_l3_agents_hosting_router(self, router_id):
+        uri = '%s/routers/%s/l3-agents' % (self.uri_prefix, router_id)
+        resp, body = self.get(uri, self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def list_service_providers(self):
+        uri = '%s/service-providers' % self.uri_prefix
+        resp, body = self.get(uri, self.headers)
+        providers = self._parse_array(etree.fromstring(body))
+        body = {'service_providers': providers}
+        return resp, body
+
 
 def _root_tag_fetcher_and_xml_to_json_parse(xml_returned_body):
     body = ET.fromstring(xml_returned_body)
diff --git a/tox.ini b/tox.ini
index a3c781b..9356dd7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -77,6 +77,11 @@
 
 [testenv:smoke]
 sitepackages = True
+commands =
+   sh tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])((smoke)|(^tempest\.scenario)) {posargs}'
+
+[testenv:smoke-serial]
+sitepackages = True
 # This is still serial because neutron doesn't work with parallel. See:
 # https://bugs.launchpad.net/tempest/+bug/1216076 so the neutron smoke
 # job would fail if we moved it to parallel.