Merge "Add tempest tests for os-aggregate update"
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 178bf62..cf838c0 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -11,7 +11,8 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import sys
+import os
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
@@ -25,7 +26,14 @@
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
+extensions = ['sphinx.ext.autodoc',
+              'sphinx.ext.intersphinx',
+              'sphinx.ext.todo',
+              'sphinx.ext.viewcode',
+              'oslo.sphinx'
+             ]
+
+todo_include_todos = True
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
@@ -41,16 +49,18 @@
 
 # General information about the project.
 project = u'Tempest'
-copyright = u'2013, Sean Dague'
+copyright = u'2013, OpenStack QA Team'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = 'havana'
+import pbr.version
+version_info = pbr.version.VersionInfo('tempest')
+version = version_info.version_string()
 # The full version, including alpha/beta/rc tags.
-release = 'havana'
+release = version_info.release_string()
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -74,17 +84,17 @@
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+add_module_names = False
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'sphinx'
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+modindex_common_prefix = ['tempest.']
 
 
 # -- Options for HTML output ---------------------------------------------------
@@ -124,7 +134,8 @@
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1"
+html_last_updated_fmt = os.popen(git_cmd).read()
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
@@ -184,7 +195,7 @@
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
   ('index', 'Tempest.tex', u'Tempest Documentation',
-   u'Sean Dague', 'manual'),
+   u'OpenStack QA Team', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -214,7 +225,7 @@
 # (source start file, name, description, authors, manual section).
 man_pages = [
     ('index', 'tempest', u'Tempest Documentation',
-     [u'Sean Dague'], 1)
+     [u'OpenStack QA Team'], 1)
 ]
 
 # If true, show URL addresses after external links.
@@ -228,7 +239,7 @@
 #  dir menu entry, description, category)
 texinfo_documents = [
   ('index', 'Tempest', u'Tempest Documentation',
-   u'Sean Dague', 'Tempest', 'One line description of project.',
+   u'OpenStack QA Team', 'Tempest', 'One line description of project.',
    'Miscellaneous'),
 ]
 
@@ -247,8 +258,8 @@
 # Bibliographic Dublin Core info.
 epub_title = u'Tempest'
 epub_author = u'Sean Dague'
-epub_publisher = u'Sean Dague'
-epub_copyright = u'2013, Sean Dague'
+epub_publisher = u'OpenStack QA Team'
+epub_copyright = u'2013, OpenStack QA Team'
 
 # The language of the text. It defaults to the language option
 # or en if the language is not set.
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index ec14c7e..115a2b5 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -222,7 +222,7 @@
 tenant_network_cidr = 10.100.0.0/16
 
 # The mask bits used to partition the tenant block.
-tenant_network_mask_bits = 28
+tenant_network_mask_bits = 24
 
 # If tenant networks are reachable, connectivity checks will be
 # performed directly against addresses on those networks.
diff --git a/requirements.txt b/requirements.txt
index b15fb92..3b9ec44 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -17,7 +17,7 @@
 testresources>=0.2.4
 keyring>=1.6.1
 testrepository>=0.0.17
-oslo.config>=1.1.0
+oslo.config>=1.2.0
 eventlet>=0.13.0
 six<1.4.0
 iso8601>=0.1.4
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 1c72eba..3c76069 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -93,6 +93,14 @@
         self.assertRaises(exceptions.NotFound,
                           self.client.get_floating_ip_details, non_exist_id)
 
+    @attr(type='gate')
+    def test_list_floating_ip_pools(self):
+        # Positive test:Should return the list of floating IP Pools
+        resp, floating_ip_pools = self.client.list_floating_ip_pools()
+        self.assertEqual(200, resp.status)
+        self.assertNotEqual(0, len(floating_ip_pools),
+                            "Expected floating IP Pools. Got zero.")
+
 
 class FloatingIPDetailsTestXML(FloatingIPDetailsTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index aec1d49..0d5a8fa 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -300,6 +300,26 @@
         self.assertEqual(202, resp.status)
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
 
+    @attr(type='gate')
+    def test_lock_unlock_server(self):
+        # Lock the server,try server stop(exceptions throw),unlock it and retry
+        resp, server = self.servers_client.lock_server(self.server_id)
+        self.assertEqual(202, resp.status)
+        resp, server = self.servers_client.get_server(self.server_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(server['status'], 'ACTIVE')
+        # Locked server is not allowed to be stopped by non-admin user
+        self.assertRaises(exceptions.BadRequest,
+                          self.servers_client.stop, self.server_id)
+        resp, server = self.servers_client.unlock_server(self.server_id)
+        self.assertEqual(202, resp.status)
+        resp, server = self.servers_client.stop(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.servers_client.wait_for_server_status(self.server_id, 'SHUTOFF')
+        resp, server = self.servers_client.start(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+
 
 class ServerActionsTestXML(ServerActionsTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index fcbde50..e9c395e 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -18,6 +18,8 @@
 
 from tempest.api.image import base
 from tempest import clients
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
 from tempest.test import attr
 
 
@@ -83,3 +85,25 @@
         self.assertEqual(200, resp.status)
         members = body['members']
         self.assertEqual(0, len(members))
+
+    @attr(type=['negative', 'gate'])
+    def test_add_member_with_non_existing_image(self):
+        # Add member with non existing image.
+        non_exist_image = rand_name('image_')
+        self.assertRaises(exceptions.NotFound, self.client.add_member,
+                          self.tenants[0], non_exist_image)
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_member_with_non_existing_image(self):
+        # Delete member with non existing image.
+        non_exist_image = rand_name('image_')
+        self.assertRaises(exceptions.NotFound, self.client.delete_member,
+                          self.tenants[0], non_exist_image)
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_member_with_non_existing_tenant(self):
+        # Delete member with non existing tenant.
+        image_id = self._create_image()
+        non_exist_tenant = rand_name('tenant_')
+        self.assertRaises(exceptions.NotFound, self.client.delete_member,
+                          non_exist_tenant, image_id)
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 7c10138..407c3ec 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -16,7 +16,6 @@
 #    under the License.
 
 import hashlib
-import time
 
 from tempest.api.object_storage import base
 from tempest.common.utils.data_utils import arbitrary_string
@@ -225,44 +224,6 @@
         self.assertEqual(resp[actual_meta_key], meta_value)
 
     @attr(type='gate')
-    def test_get_object_using_temp_url(self):
-        # access object using temporary URL within expiration time
-
-        try:
-            # update account metadata
-            # flag to check if account metadata got updated
-            flag = False
-            key = 'Meta'
-            metadata = {'Temp-URL-Key': key}
-            resp, _ = self.account_client.create_account_metadata(
-                metadata=metadata)
-            self.assertIn(int(resp['status']), HTTP_SUCCESS)
-            flag = True
-            resp, _ = self.account_client.list_account_metadata()
-            self.assertIn('x-account-meta-temp-url-key', resp)
-            self.assertEqual(resp['x-account-meta-temp-url-key'], key)
-
-            # create object
-            object_name = rand_name(name='ObjectTemp')
-            data = arbitrary_string(size=len(object_name),
-                                    base_text=object_name)
-            self.object_client.create_object(self.container_name,
-                                             object_name, data)
-            expires = int(time.time() + 10)
-
-            # trying to get object using temp url with in expiry time
-            _, body = self.object_client.get_object_using_temp_url(
-                self.container_name, object_name,
-                expires, key)
-            self.assertEqual(body, data)
-        finally:
-            if flag:
-                resp, _ = self.account_client.delete_account_metadata(
-                    metadata=metadata)
-                resp, _ = self.account_client.list_account_metadata()
-                self.assertNotIn('x-account-meta-temp-url-key', resp)
-
-    @attr(type='gate')
     def test_object_upload_in_segments(self):
         # create object
         object_name = rand_name(name='LObject')
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
new file mode 100644
index 0000000..0fd5499
--- /dev/null
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -0,0 +1,157 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+#
+# Author: Joe H. Rahme <joe.hakim.rahme@enovance.com>
+#
+# 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 hashlib
+import hmac
+import time
+import urlparse
+
+from tempest.api.object_storage import base
+from tempest.common.utils.data_utils import arbitrary_string
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
+from tempest.test import attr
+from tempest.test import HTTP_SUCCESS
+
+
+class ObjectTempUrlTest(base.BaseObjectTest):
+
+    @classmethod
+    def setUpClass(cls):
+        super(ObjectTempUrlTest, cls).setUpClass()
+        cls.container_name = rand_name(name='TestContainer')
+        cls.container_client.create_container(cls.container_name)
+        cls.containers = [cls.container_name]
+
+        # update account metadata
+        cls.key = 'Meta'
+        cls.metadata = {'Temp-URL-Key': cls.key}
+        cls.account_client.create_account_metadata(metadata=cls.metadata)
+        cls.account_client_metadata, _ = \
+            cls.account_client.list_account_metadata()
+
+    @classmethod
+    def tearDownClass(cls):
+        resp, _ = cls.account_client.delete_account_metadata(
+            metadata=cls.metadata)
+        resp, _ = cls.account_client.list_account_metadata()
+
+        cls.delete_containers(cls.containers)
+        # delete the user setup created
+        cls.data.teardown_all()
+        super(ObjectTempUrlTest, cls).tearDownClass()
+
+    def setUp(self):
+        super(ObjectTempUrlTest, self).setUp()
+        # make sure the metadata has been set
+        self.assertIn('x-account-meta-temp-url-key',
+                      self.account_client_metadata)
+
+        self.assertEqual(
+            self.account_client_metadata['x-account-meta-temp-url-key'],
+            self.key)
+
+        # create object
+        self.object_name = rand_name(name='ObjectTemp')
+        self.data = arbitrary_string(size=len(self.object_name),
+                                     base_text=self.object_name)
+        self.object_client.create_object(self.container_name,
+                                         self.object_name, self.data)
+
+    def get_temp_url(self, container, object_name, method, expires,
+                     key):
+        """Create the temporary URL."""
+
+        path = "%s/%s/%s" % (
+            urlparse.urlparse(self.object_client.base_url).path,
+            container, object_name)
+
+        hmac_body = '%s\n%s\n%s' % (method, expires, path)
+        sig = hmac.new(key, hmac_body, hashlib.sha1).hexdigest()
+
+        url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container,
+                                                             object_name,
+                                                             sig, expires)
+
+        return url
+
+    @attr(type='gate')
+    def test_get_object_using_temp_url(self):
+        EXPIRATION_TIME = 10000  # high to ensure the test finishes.
+        expires = int(time.time() + EXPIRATION_TIME)
+
+        # get a temp URL for the created object
+        url = self.get_temp_url(self.container_name,
+                                self.object_name, "GET",
+                                expires, self.key)
+
+        # trying to get object using temp url within expiry time
+        _, body = self.object_client.get_object_using_temp_url(url)
+
+        self.assertEqual(body, self.data)
+
+        # Testing a HEAD on this Temp URL
+        resp, body = self.object_client.head(url)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+
+    @attr(type='gate')
+    def test_put_object_using_temp_url(self):
+        # make sure the metadata has been set
+        new_data = arbitrary_string(size=len(self.object_name),
+                                    base_text=rand_name(name="random"))
+
+        EXPIRATION_TIME = 10000  # high to ensure the test finishes.
+        expires = int(time.time() + EXPIRATION_TIME)
+
+        url = self.get_temp_url(self.container_name,
+                                self.object_name, "PUT",
+                                expires, self.key)
+
+        # trying to put random data in the object using temp url
+        resp, body = self.object_client.put_object_using_temp_url(
+            url, new_data)
+
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+
+        # Testing a HEAD on this Temp URL
+        resp, body = self.object_client.head(url)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+
+        # Validate that the content of the object has been modified
+        url = self.get_temp_url(self.container_name,
+                                self.object_name, "GET",
+                                expires, self.key)
+
+        _, body = self.object_client.get_object_using_temp_url(url)
+        self.assertEqual(body, new_data)
+
+    @attr(type=['gate', 'negative'])
+    def test_get_object_after_expiration_time(self):
+        EXPIRATION_TIME = 1
+        expires = int(time.time() + EXPIRATION_TIME)
+
+        # get a temp URL for the created object
+        url = self.get_temp_url(self.container_name,
+                                self.object_name, "GET",
+                                expires, self.key)
+
+        # temp URL is valid for 1 seconds, let's wait 3
+        time.sleep(EXPIRATION_TIME + 2)
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.object_client.get_object_using_temp_url,
+                          url)
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index d6b4466..8c82ec0 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -14,7 +14,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import keystoneclient.v2_0.client
+import netaddr
+
+import keystoneclient.v2_0.client as keystoneclient
+import neutronclient.v2_0.client as neutronclient
 
 from tempest import clients
 from tempest.common.utils.data_utils import rand_name
@@ -30,97 +33,118 @@
     def __init__(self, name, tempest_client=True, interface='json',
                  password='pass'):
         self.isolated_creds = {}
+        self.isolated_net_resources = {}
+        self.ports = []
         self.name = name
         self.config = config.TempestConfig()
         self.tempest_client = tempest_client
         self.interface = interface
         self.password = password
-        self.admin_client = self._get_identity_admin_client()
+        self.identity_admin_client, self.network_admin_client = (
+            self._get_admin_clients())
 
-    def _get_keystone_client(self):
+    def _get_official_admin_clients(self):
         username = self.config.identity.admin_username
         password = self.config.identity.admin_password
         tenant_name = self.config.identity.admin_tenant_name
         auth_url = self.config.identity.uri
         dscv = self.config.identity.disable_ssl_certificate_validation
-        return keystoneclient.v2_0.client.Client(username=username,
-                                                 password=password,
-                                                 tenant_name=tenant_name,
-                                                 auth_url=auth_url,
-                                                 insecure=dscv)
+        identity_client = keystoneclient.Client(username=username,
+                                                password=password,
+                                                tenant_name=tenant_name,
+                                                auth_url=auth_url,
+                                                insecure=dscv)
+        network_client = neutronclient.Client(username=username,
+                                              password=password,
+                                              tenant_name=tenant_name,
+                                              auth_url=auth_url,
+                                              insecure=dscv)
+        return identity_client, network_client
 
-    def _get_identity_admin_client(self):
+    def _get_admin_clients(self):
         """
-        Returns an instance of the Identity Admin API client
+        Returns a tuple with instances of the following admin clients (in this
+        order):
+            identity
+            network
         """
         if self.tempest_client:
             os = clients.AdminManager(interface=self.interface)
-            admin_client = os.identity_client
+            admin_clients = (os.identity_client,
+                             os.network_client,)
         else:
-            admin_client = self._get_keystone_client()
-        return admin_client
+            admin_clients = self._get_official_admin_clients()
+        return admin_clients
 
     def _create_tenant(self, name, description):
         if self.tempest_client:
-            resp, tenant = self.admin_client.create_tenant(
+            resp, tenant = self.identity_admin_client.create_tenant(
                 name=name, description=description)
         else:
-            tenant = self.admin_client.tenants.create(name,
-                                                      description=description)
+            tenant = self.identity_admin_client.tenants.create(
+                name,
+                description=description)
         return tenant
 
     def _get_tenant_by_name(self, name):
         if self.tempest_client:
-            resp, tenant = self.admin_client.get_tenant_by_name(name)
+            resp, tenant = self.identity_admin_client.get_tenant_by_name(name)
         else:
-            tenants = self.admin_client.tenants.list()
+            tenants = self.identity_admin_client.tenants.list()
             for ten in tenants:
                 if ten['name'] == name:
                     tenant = ten
-            raise exceptions.NotFound('No such tenant')
+                    break
+            else:
+                raise exceptions.NotFound('No such tenant')
         return tenant
 
     def _create_user(self, username, password, tenant, email):
         if self.tempest_client:
-            resp, user = self.admin_client.create_user(username, password,
-                                                       tenant['id'], email)
+            resp, user = self.identity_admin_client.create_user(username,
+                                                                password,
+                                                                tenant['id'],
+                                                                email)
         else:
-            user = self.admin_client.users.create(username, password, email,
-                                                  tenant_id=tenant.id)
+            user = self.identity_admin_client.users.create(username, password,
+                                                           email,
+                                                           tenant_id=tenant.id)
         return user
 
     def _get_user(self, tenant, username):
         if self.tempest_client:
-            resp, user = self.admin_client.get_user_by_username(tenant['id'],
-                                                                username)
+            resp, user = self.identity_admin_client.get_user_by_username(
+                tenant['id'],
+                username)
         else:
-            user = self.admin_client.users.get(username)
+            user = self.identity_admin_client.users.get(username)
         return user
 
     def _list_roles(self):
         if self.tempest_client:
-            resp, roles = self.admin_client.list_roles()
+            resp, roles = self.identity_admin_client.list_roles()
         else:
-            roles = self.admin_client.roles.list()
+            roles = self.identity_admin_client.roles.list()
         return roles
 
     def _assign_user_role(self, tenant, user, role):
         if self.tempest_client:
-            self.admin_client.assign_user_role(tenant, user, role)
+            self.identity_admin_client.assign_user_role(tenant, user, role)
         else:
-            self.admin_client.roles.add_user_role(user, role, tenant=tenant)
+            self.identity_admin_client.roles.add_user_role(user,
+                                                           role, tenant=tenant)
 
     def _delete_user(self, user):
         if self.tempest_client:
-            self.admin_client.delete_user(user)
+            self.identity_admin_client.delete_user(user)
         else:
-            self.admin_client.users.delete(user)
+            self.identity_admin_client.users.delete(user)
 
     def _delete_tenant(self, tenant):
         if self.tempest_client:
-            self.admin_client.delete_tenant(tenant)
+            self.identity_admin_client.delete_tenant(tenant)
         else:
-            self.admin_client.tenants.delete(tenant)
+            self.identity_admin_client.tenants.delete(tenant)
 
     def _create_creds(self, suffix=None, admin=False):
         rand_name_root = rand_name(self.name)
@@ -128,7 +152,6 @@
             rand_name_root += suffix
         tenant_name = rand_name_root + "-tenant"
         tenant_desc = tenant_name + "-desc"
-        rand_name_root = rand_name(self.name)
         tenant = self._create_tenant(name=tenant_name,
                                      description=tenant_desc)
         if suffix:
@@ -164,6 +187,92 @@
             tenant_name = tenant.name
         return username, tenant_name
 
+    def _get_tenant_id(self, tenant):
+        if self.tempest_client:
+            return tenant.get('id')
+        else:
+            return tenant.id
+
+    def _create_network_resources(self, tenant_id):
+        network = None
+        subnet = None
+        router = None
+        rand_name_root = rand_name(self.name)
+        network_name = rand_name_root + "-network"
+        network = self._create_network(network_name, tenant_id)
+        try:
+            subnet_name = rand_name_root + "-subnet"
+            subnet = self._create_subnet(subnet_name, tenant_id, network['id'])
+            router_name = rand_name_root + "-router"
+            router = self._create_router(router_name, tenant_id)
+            self._add_router_interface(router['id'], subnet['id'])
+        except Exception:
+            if router:
+                self._clear_isolated_router(router['id'], router['name'])
+            if subnet:
+                self._clear_isolated_subnet(subnet['id'], subnet['name'])
+            if network:
+                self._clear_isolated_network(network['id'], network['name'])
+            raise
+        return network, subnet, router
+
+    def _create_network(self, name, tenant_id):
+        if self.tempest_client:
+            resp, resp_body = self.network_admin_client.create_network(
+                name, tenant_id=tenant_id)
+        else:
+            body = {'network': {'tenant_id': tenant_id, 'name': name}}
+            resp_body = self.network_admin_client.create_network(body)
+        return resp_body['network']
+
+    def _create_subnet(self, subnet_name, tenant_id, network_id):
+        if not self.tempest_client:
+            body = {'subnet': {'name': subnet_name, 'tenant_id': tenant_id,
+                               'network_id': network_id, 'ip_version': 4}}
+        base_cidr = netaddr.IPNetwork(self.config.network.tenant_network_cidr)
+        mask_bits = self.config.network.tenant_network_mask_bits
+        for subnet_cidr in base_cidr.subnet(mask_bits):
+            try:
+                if self.tempest_client:
+                    resp, resp_body = self.network_admin_client.create_subnet(
+                        network_id, str(subnet_cidr), name=subnet_name,
+                        tenant_id=tenant_id)
+                else:
+                    body['subnet']['cidr'] = str(subnet_cidr)
+                    resp_body = self.network_admin_client.create_subnet(body)
+                break
+            except exceptions.BadRequest as e:
+                if 'overlaps with another subnet' not in str(e):
+                    raise
+        else:
+            e = exceptions.BuildErrorException()
+            e.message = 'Available CIDR for subnet creation could not be found'
+            raise e
+        return resp_body['subnet']
+
+    def _create_router(self, router_name, tenant_id):
+        external_net_id = dict(
+            network_id=self.config.network.public_network_id)
+        if self.tempest_client:
+            resp, resp_body = self.network_admin_client.create_router(
+                router_name,
+                external_gateway_info=external_net_id,
+                tenant_id=tenant_id)
+        else:
+            body = {'router': {'name': router_name, 'tenant_id': tenant_id,
+                               'external_gateway_info': external_net_id,
+                               'admin_state_up': True}}
+            resp_body = self.network_admin_client.create_router(body)
+        return resp_body['router']
+
+    def _add_router_interface(self, router_id, subnet_id):
+        if self.tempest_client:
+            self.network_admin_client.add_router_interface_with_subnet_id(
+                router_id, subnet_id)
+        else:
+            body = {'subnet_id': subnet_id}
+            self.network_admin_client.add_interface_router(router_id, body)
+
     def get_primary_tenant(self):
         return self.isolated_creds.get('primary')[1]
 
@@ -182,6 +291,33 @@
     def get_admin_user(self):
         return self.isolated_creds.get('admin')[0]
 
+    def get_primary_network(self):
+        return self.isolated_net_resources.get('primary')[0]
+
+    def get_primary_subnet(self):
+        return self.isolated_net_resources.get('primary')[1]
+
+    def get_primary_router(self):
+        return self.isolated_net_resources.get('primary')[2]
+
+    def get_admin_network(self):
+        return self.isolated_net_resources.get('admin')[0]
+
+    def get_admin_subnet(self):
+        return self.isolated_net_resources.get('admin')[1]
+
+    def get_admin_router(self):
+        return self.isolated_net_resources.get('admin')[2]
+
+    def get_alt_network(self):
+        return self.isolated_net_resources.get('alt')[0]
+
+    def get_alt_subnet(self):
+        return self.isolated_net_resources.get('alt')[1]
+
+    def get_alt_router(self):
+        return self.isolated_net_resources.get('alt')[2]
+
     def get_primary_creds(self):
         if self.isolated_creds.get('primary'):
             user, tenant = self.isolated_creds['primary']
@@ -190,8 +326,15 @@
             user, tenant = self._create_creds()
             username, tenant_name = self._get_cred_names(user, tenant)
             self.isolated_creds['primary'] = (user, tenant)
-            LOG.info("Aquired isolated creds:\n user: %s, tenant: %s"
+            LOG.info("Acquired isolated creds:\n user: %s, tenant: %s"
                      % (username, tenant_name))
+            if self.config.service_available.neutron:
+                network, subnet, router = self._create_network_resources(
+                    self._get_tenant_id(tenant))
+                self.isolated_net_resources['primary'] = (
+                    network, subnet, router,)
+                LOG.info("Created isolated network resources for : \n"
+                         + " user: %s, tenant: %s" % (username, tenant_name))
         return username, tenant_name, self.password
 
     def get_admin_creds(self):
@@ -202,9 +345,16 @@
             user, tenant = self._create_creds(admin=True)
             username, tenant_name = self._get_cred_names(user, tenant)
             self.isolated_creds['admin'] = (user, tenant)
-            LOG.info("Aquired admin isolated creds:\n user: %s, tenant: %s"
+            LOG.info("Acquired admin isolated creds:\n user: %s, tenant: %s"
                      % (username, tenant_name))
-            return username, tenant_name, self.password
+            if self.config.service_available.neutron:
+                network, subnet, router = self._create_network_resources(
+                    self._get_tenant_id(tenant))
+                self.isolated_net_resources['admin'] = (
+                    network, subnet, router,)
+                LOG.info("Created isolated network resources for : \n"
+                         + " user: %s, tenant: %s" % (username, tenant_name))
+        return username, tenant_name, self.password
 
     def get_alt_creds(self):
         if self.isolated_creds.get('alt'):
@@ -214,13 +364,93 @@
             user, tenant = self._create_creds()
             username, tenant_name = self._get_cred_names(user, tenant)
             self.isolated_creds['alt'] = (user, tenant)
-            LOG.info("Aquired alt isolated creds:\n user: %s, tenant: %s"
+            LOG.info("Acquired alt isolated creds:\n user: %s, tenant: %s"
                      % (username, tenant_name))
+            if self.config.service_available.neutron:
+                network, subnet, router = self._create_network_resources(
+                    self._get_tenant_id(tenant))
+                self.isolated_net_resources['alt'] = (
+                    network, subnet, router,)
+                LOG.info("Created isolated network resources for : \n"
+                         + " user: %s, tenant: %s" % (username, tenant_name))
         return username, tenant_name, self.password
 
+    def _clear_isolated_router(self, router_id, router_name):
+        net_client = self.network_admin_client
+        try:
+            net_client.delete_router(router_id)
+        except exceptions.NotFound:
+            LOG.warn('router with name: %s not found for delete' %
+                     router_name)
+            pass
+
+    def _clear_isolated_subnet(self, subnet_id, subnet_name):
+        net_client = self.network_admin_client
+        try:
+            net_client.delete_subnet(subnet_id)
+        except exceptions.NotFound:
+            LOG.warn('subnet with name: %s not found for delete' %
+                     subnet_name)
+            pass
+
+    def _clear_isolated_network(self, network_id, network_name):
+        net_client = self.network_admin_client
+        try:
+            net_client.delete_network(network_id)
+        except exceptions.NotFound:
+            LOG.warn('network with name: %s not found for delete' %
+                     network_name)
+            pass
+
+    def _cleanup_ports(self, network_id):
+        # TODO(mlavalle) This method will be removed once patch
+        # https://review.openstack.org/#/c/46563/ merges in Neutron
+        if not self.ports:
+            if self.tempest_client:
+                resp, resp_body = self.network_admin_client.list_ports()
+            else:
+                resp_body = self.network_admin_client.list_ports()
+            self.ports = resp_body['ports']
+        ports_to_delete = [
+            port for port in self.ports if port['network_id'] == network_id]
+        for port in ports_to_delete:
+            try:
+                LOG.info('Cleaning up port id %s, name %s' %
+                         (port['id'], port['name']))
+                self.network_admin_client.delete_port(port['id'])
+            except exceptions.NotFound:
+                LOG.warn('Port id: %s, name %s not found for clean-up' %
+                         (port['id'], port['name']))
+
+    def _clear_isolated_net_resources(self):
+        net_client = self.network_admin_client
+        for cred in self.isolated_net_resources:
+            network, subnet, router = self.isolated_net_resources.get(cred)
+            try:
+                if self.tempest_client:
+                    net_client.remove_router_interface_with_subnet_id(
+                        router['id'], subnet['id'])
+                else:
+                    body = {'subnet_id': subnet['id']}
+                    net_client.remove_interface_router(router['id'], body)
+            except exceptions.NotFound:
+                LOG.warn('router with name: %s not found for delete' %
+                         router['name'])
+                pass
+            self._clear_isolated_router(router['id'], router['name'])
+            # TODO(mlavalle) This method call will be removed once patch
+            # https://review.openstack.org/#/c/46563/ merges in Neutron
+            self._cleanup_ports(network['id'])
+            self._clear_isolated_subnet(subnet['id'], subnet['name'])
+            self._clear_isolated_network(network['id'], network['name'])
+            LOG.info("Cleared isolated network resources: \n"
+                     + " network: %s, subnet: %s, router: %s"
+                     % (network['name'], subnet['name'], router['name']))
+
     def clear_isolated_creds(self):
         if not self.isolated_creds:
             return
+        self._clear_isolated_net_resources()
         for cred in self.isolated_creds:
             user, tenant = self.isolated_creds.get(cred)
             try:
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index a867a12..4b5127a 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -80,6 +80,21 @@
         self.http_obj = http.ClosingHttp(
             disable_ssl_certificate_validation=dscv)
 
+    def __str__(self):
+        STRING_LIMIT = 80
+        str_format = ("config:%s, user:%s, password:%s, "
+                      "auth_url:%s, tenant_name:%s, auth_version:%s, "
+                      "service:%s, base_url:%s, region:%s, "
+                      "endpoint_url:%s, build_interval:%s, build_timeout:%s"
+                      "\ntoken:%s..., \nheaders:%s...")
+        return str_format % (self.config, self.user, self.password,
+                             self.auth_url, self.tenant_name,
+                             self.auth_version, self.service,
+                             self.base_url, self.region, self.endpoint_url,
+                             self.build_interval, self.build_timeout,
+                             str(self.token)[0:STRING_LIMIT],
+                             str(self.headers)[0:STRING_LIMIT])
+
     def _set_auth(self):
         """
         Sets the token and base_url used in requests based on the strategy type
@@ -448,8 +463,8 @@
         # NOTE(mtreinish): This is for compatibility with Glance and swift
         # APIs. These are the return content types that Glance api v1
         # (and occasionally swift) are using.
-        TXT_ENC = ['text/plain; charset=UTF-8', 'text/html; charset=UTF-8',
-                   'text/plain; charset=utf-8']
+        TXT_ENC = ['text/plain', 'text/plain; charset=UTF-8',
+                   'text/html; charset=UTF-8', 'text/plain; charset=utf-8']
         XML_ENC = ['application/xml', 'application/xml; charset=UTF-8']
 
         if ctype in JSON_ENC or ctype in XML_ENC:
diff --git a/tempest/config.py b/tempest/config.py
index 71705fb..eadbe9a 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -182,8 +182,8 @@
                     "succeed."),
     cfg.IntOpt('ready_wait',
                default=0,
-               help="Additinal wait time for clean state, when there is"
-                    " no OS-EXT-STS extension availiable"),
+               help="Additional wait time for clean state, when there is "
+                    "no OS-EXT-STS extension available"),
     cfg.IntOpt('ssh_channel_timeout',
                default=60,
                help="Timeout in seconds to wait for output from ssh "
@@ -273,7 +273,7 @@
     cfg.StrOpt('http_image',
                default='http://download.cirros-cloud.net/0.3.1/'
                'cirros-0.3.1-x86_64-uec.tar.gz',
-               help='http accessable image')
+               help='http accessible image')
 ]
 
 
@@ -702,6 +702,7 @@
         register_stress_opts(cfg.CONF)
         register_scenario_opts(cfg.CONF)
         register_service_available_opts(cfg.CONF)
+        register_debug_opts(cfg.CONF)
         self.compute = cfg.CONF.compute
         self.identity = cfg.CONF.identity
         self.images = cfg.CONF.image
@@ -715,6 +716,7 @@
         self.stress = cfg.CONF.stress
         self.scenario = cfg.CONF.scenario
         self.service_available = cfg.CONF.service_available
+        self.debug = cfg.CONF.debug
         if not self.compute_admin.username:
             self.compute_admin.username = self.identity.admin_username
             self.compute_admin.password = self.identity.admin_password
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 44788f2..c01de83 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -478,10 +478,13 @@
     @classmethod
     def setUpClass(cls):
         super(NetworkScenarioTest, cls).setUpClass()
-        cls.tenant_id = cls.manager._get_identity_client(
-            cls.config.identity.username,
-            cls.config.identity.password,
-            cls.config.identity.tenant_name).tenant_id
+        if cls.config.compute.allow_tenant_isolation:
+            cls.tenant_id = cls.isolated_creds.get_primary_tenant().id
+        else:
+            cls.tenant_id = cls.manager._get_identity_client(
+                cls.config.identity.username,
+                cls.config.identity.password,
+                cls.config.identity.tenant_name).tenant_id
 
     def _create_security_group(self, client=None, namestart='secgroup-smoke-'):
         if client is None:
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 7f7ba91..6cd9fe8 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -110,10 +110,6 @@
     def setUpClass(cls):
         super(TestNetworkBasicOps, cls).setUpClass()
         cls.check_preconditions()
-        cls.tenant_id = cls.manager._get_identity_client(
-            cls.config.identity.username,
-            cls.config.identity.password,
-            cls.config.identity.tenant_name).tenant_id
         # TODO(mnewby) Consider looking up entities as needed instead
         # of storing them as collections on the class.
         cls.keypairs = {}
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index 769c538..8349263 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -94,3 +94,13 @@
         except exceptions.NotFound:
             return True
         return False
+
+    def list_floating_ip_pools(self, params=None):
+        """Returns a list of all floating IP Pools."""
+        url = 'os-floating-ip-pools'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['floating_ip_pools']
diff --git a/tempest/services/compute/xml/floating_ips_client.py b/tempest/services/compute/xml/floating_ips_client.py
index 278cc88..2fffaa2 100644
--- a/tempest/services/compute/xml/floating_ips_client.py
+++ b/tempest/services/compute/xml/floating_ips_client.py
@@ -107,3 +107,13 @@
         except exceptions.NotFound:
             return True
         return False
+
+    def list_floating_ip_pools(self, params=None):
+        """Returns a list of all floating IP Pools."""
+        url = 'os-floating-ip-pools'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index bf72bdc..9b688aa 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -193,6 +193,14 @@
         server = self._parse_server(etree.fromstring(body))
         return resp, server
 
+    def lock_server(self, server_id, **kwargs):
+        """Locks the given server."""
+        return self.action(server_id, 'lock', None, **kwargs)
+
+    def unlock_server(self, server_id, **kwargs):
+        """Unlocks the given server."""
+        return self.action(server_id, 'unlock', None, **kwargs)
+
     def suspend_server(self, server_id, **kwargs):
         """Suspends the provided server."""
         return self.action(server_id, 'suspend', None, **kwargs)
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index c2d701d..4b87b91 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -45,12 +45,9 @@
         body = json.loads(body)
         return resp, body
 
-    def create_network(self, name):
-        post_body = {
-            'network': {
-                'name': name,
-            }
-        }
+    def create_network(self, name, **kwargs):
+        post_body = {'network': kwargs}
+        post_body['network']['name'] = name
         body = json.dumps(post_body)
         uri = '%s/networks' % (self.uri_prefix)
         resp, body = self.post(uri, headers=self.headers, body=body)
@@ -79,12 +76,11 @@
         resp, body = self.delete(uri, self.headers)
         return resp, body
 
-    def create_subnet(self, net_uuid, cidr):
-        post_body = dict(
-            subnet=dict(
-                ip_version=4,
-                network_id=net_uuid,
-                cidr=cidr),)
+    def create_subnet(self, net_uuid, cidr, ip_version=4, **kwargs):
+        post_body = {'subnet': kwargs}
+        post_body['subnet']['ip_version'] = ip_version
+        post_body['subnet']['network_id'] = net_uuid
+        post_body['subnet']['cidr'] = cidr
         body = json.dumps(post_body)
         uri = '%s/subnets' % (self.uri_prefix)
         resp, body = self.post(uri, headers=self.headers, body=body)
@@ -206,16 +202,10 @@
         body = json.loads(body)
         return resp, body
 
-    def create_router(self, name, **kwargs):
-        post_body = {
-            'router': {
-                'name': name,
-            }
-        }
-        post_body['router']['admin_state_up'] = kwargs.get(
-            'admin_state_up', True)
-        post_body['router']['external_gateway_info'] = kwargs.get(
-            'external_gateway_info', None)
+    def create_router(self, name, admin_state_up=True, **kwargs):
+        post_body = {'router': kwargs}
+        post_body['router']['name'] = name
+        post_body['router']['admin_state_up'] = admin_state_up
         body = json.dumps(post_body)
         uri = '%s/routers' % (self.uri_prefix)
         resp, body = self.post(uri, headers=self.headers, body=body)
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index d338d45..2fee042 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -15,10 +15,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import hashlib
-import hmac
-import urlparse
-
 from tempest.common import http
 from tempest.common.rest_client import RestClient
 from tempest import exceptions
@@ -125,29 +121,20 @@
         resp, body = self.copy(url, headers=headers)
         return resp, body
 
-    def get_object_using_temp_url(self, container, object_name, expires, key):
-        """Retrieve object's data using temporary URL."""
-
-        self._set_auth()
-        method = 'GET'
-        path = "%s/%s/%s" % (urlparse.urlparse(self.base_url).path, container,
-                             object_name)
-        hmac_body = '%s\n%s\n%s' % (method, expires, path)
-        sig = hmac.new(key, hmac_body, hashlib.sha1).hexdigest()
-
-        url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container,
-                                                             object_name,
-                                                             sig, expires)
-
-        resp, body = self.get(url)
-        return resp, body
-
     def create_object_segments(self, container, object_name, segment, data):
         """Creates object segments."""
         url = "{0}/{1}/{2}".format(container, object_name, segment)
         resp, body = self.put(url, data, self.headers)
         return resp, body
 
+    def get_object_using_temp_url(self, url):
+        """Retrieve object's data using temp URL."""
+        return self.get(url)
+
+    def put_object_using_temp_url(self, url, data):
+        """Put data in an object using temp URL."""
+        return self.put(url, data, None)
+
 
 class ObjectClientCustomizedHeader(RestClient):
 
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index d959543..8209f17 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -35,11 +35,14 @@
     username = admin_manager.config.stress.target_ssh_user
     key_filename = admin_manager.config.stress.target_private_key_path
     if not (username and key_filename):
+        LOG.error('username and key_filename should not be empty')
         return None
     ssh_client = ssh.Client(host, username, key_filename=key_filename)
     try:
         return ssh_client.exec_command(command)
     except exceptions.SSHExecCommandFailed:
+        LOG.error('do_ssh raise exception. command:%s, host:%s.'
+                  % (command, host))
         return None
 
 
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index 486e0a0..bce544a 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -188,13 +188,13 @@
         self.assertEqual(tags[0].value, 'value1')
 
         tags = self.ec2_client.get_all_tags(filters={'key': 'value2'})
-        self.assertEqual(len(tags), 0)
+        self.assertEqual(len(tags), 0, str(tags))
 
         for instance in reservation.instances:
             instance.remove_tag('key1', value='value1')
 
         tags = self.ec2_client.get_all_tags()
-        self.assertEqual(len(tags), 0)
+        self.assertEqual(len(tags), 0, str(tags))
 
         for instance in reservation.instances:
             instance.stop()
diff --git a/test-requirements.txt b/test-requirements.txt
index 1ede25e..8aa6ed9 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,3 +3,4 @@
 docutils==0.9.1
 sphinx>=1.1.2
 python-subunit
+oslo.sphinx