Merge "Add new test for the volume upload in Glance functionality"
diff --git a/HACKING.rst b/HACKING.rst
index 1db1e26..d69f935 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -157,6 +157,52 @@
                              kwarg2=dict_of_numbers)
 
 
+Exception Handling
+------------------
+According to the ``The Zen of Python`` the
+ ``Errors should never pass silently.``
+Tempest usually runs in special environment (jenkins gate jobs), in every
+error or failure situation we should provide as much error related
+information as possible, because we usually do not have the chance to
+investigate the situation after the issue happened.
+
+In every test case the abnormal situations must be very verbosely explained,
+by the exception and the log.
+
+In most cases the very first issue is the most important information.
+
+Try to avoid using ``try`` blocks in the test cases, both the ``except``
+and ``finally`` block could replace the original exception,
+when the additional operations leads to another exception.
+
+Just letting an exception to propagate, is not bad idea in a test case,
+ at all.
+
+Try to avoid using any exception handling construct which can hide the errors
+origin.
+
+If you really need to use a ``try`` block, please ensure the original
+exception at least logged.  When the exception is logged you usually need
+to ``raise`` the same or a different exception anyway.
+
+Use the ``self.assert*`` methods provided by the unit test framework
+ the signal failures early.
+
+Avoid using the ``self.fail`` alone, it's stack trace will signal
+ the ``self.fail`` line as the origin of the error.
+
+Avoid constructing complex boolean expressions for assertion.
+The ``self.assertTrue`` or ``self.assertFalse`` will just tell you the
+single boolean, and you will not know anything about the values used in
+the formula. Most other assert method can include more information.
+For example ``self.assertIn`` can include the whole set.
+
+If the test case fails you can see the related logs and the information
+carried by the exception (exception class, backtrack and exception info).
+This and the service logs are your only guide to find the root cause of flaky
+issue.
+
+
 Test Skips
 ----------
 If a test is broken because of a bug it is appropriate to skip the test until
diff --git a/doc/source/conf.py b/doc/source/conf.py
index fd8cbb5..178bf62 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -138,10 +138,10 @@
 #html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+html_domain_indices = False
 
 # If false, no index is generated.
-#html_use_index = True
+html_use_index = False
 
 # If true, the index is split into individual pages for each letter.
 #html_split_index = False
diff --git a/doc/source/index.rst b/doc/source/index.rst
index e8fdf2c..f012097 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -38,6 +38,4 @@
 Indices and tables
 ==================
 
-* :ref:`genindex`
-* :ref:`modindex`
 * :ref:`search`
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 617c016..12aa399 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -124,11 +124,11 @@
 # relax-xsm-sr-check
 block_migrate_supports_cinder_iscsi = false
 
-# By default, rely on the status of the diskConfig extension to
-# decide if to execute disk config tests. When set to false, tests
-# are forced to skip, regardless of the extension status
-disk_config_enabled_override = true
+# When set to false, disk config tests are forced to skip
+disk_config_enabled = true
 
+# When set to false, flavor extra data tests are forced to skip
+flavor_extra_enabled = true
 
 [whitebox]
 # Whitebox options for compute. Whitebox options enable the
@@ -181,9 +181,9 @@
 # This section contains configuration options used when executing tests
 # against the OpenStack Network API.
 
-# Version of the Quantum API
+# Version of the Neutron API
 api_version = v1.1
-# Catalog type of the Quantum Service
+# Catalog type of the Neutron Service
 catalog_type = network
 
 # A large private cidr block from which to allocate smaller blocks for
@@ -206,8 +206,8 @@
 # for each tenant to have their own router.
 public_router_id = {$PUBLIC_ROUTER_ID}
 
-# Whether or not quantum is expected to be available
-quantum_available = false
+# Whether or not neutron is expected to be available
+neutron_available = false
 
 [volume]
 # This section contains the configuration options used when executing tests
@@ -229,6 +229,10 @@
 multi_backend_enabled = false
 backend1_name = BACKEND_1
 backend2_name = BACKEND_2
+# Protocol and vendor of volume backend to target when testing volume-types.
+# You should update to reflect those exported by configured backend driver.
+storage_protocol = iSCSI
+vendor_name = Open Source
 
 [object-storage]
 # This section contains configuration options used when executing tests
@@ -331,3 +335,9 @@
 
 # ssh username for the image file
 ssh_user = cirros
+
+[cli]
+# Enable cli tests
+enabled = True
+# directory where python client binaries are located
+cli_dir = /usr/local/bin
diff --git a/requirements.txt b/requirements.txt
index 19d6e0b..b3c706b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,7 @@
 anyjson
 nose
 httplib2>=0.7.0
-testtools>=0.9.29
+testtools>=0.9.32
 lxml
 boto>=2.2.1
 paramiko
@@ -11,7 +11,7 @@
 python-glanceclient>=0.5.0
 python-keystoneclient>=0.2.0
 python-novaclient>=2.10.0
-python-quantumclient>=2.1
+python-neutronclient>=2.2.3,<3.0.0
 python-cinderclient>=1.0.4,<2
 testresources
 keyring
@@ -19,4 +19,3 @@
 oslo.config>=1.1.0
 # Needed for whitebox testing
 sqlalchemy
-MySQL-python
diff --git a/tempest/api/compute/__init__.py b/tempest/api/compute/__init__.py
index 98f198d..fb96b4a 100644
--- a/tempest/api/compute/__init__.py
+++ b/tempest/api/compute/__init__.py
@@ -15,7 +15,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import clients
 from tempest.common import log as logging
 from tempest import config
 from tempest.exceptions import InvalidConfiguration
@@ -26,9 +25,8 @@
 CREATE_IMAGE_ENABLED = CONFIG.compute.create_image_enabled
 RESIZE_AVAILABLE = CONFIG.compute.resize_available
 CHANGE_PASSWORD_AVAILABLE = CONFIG.compute.change_password_available
-DISK_CONFIG_ENABLED = True
-DISK_CONFIG_ENABLED_OVERRIDE = CONFIG.compute.disk_config_enabled_override
-FLAVOR_EXTRA_DATA_ENABLED = True
+DISK_CONFIG_ENABLED = CONFIG.compute.disk_config_enabled
+FLAVOR_EXTRA_DATA_ENABLED = CONFIG.compute.flavor_extra_enabled
 MULTI_USER = True
 
 
@@ -36,27 +34,7 @@
 def generic_setup_package():
     LOG.debug("Entering tempest.api.compute.setup_package")
 
-    global MULTI_USER, DISK_CONFIG_ENABLED, FLAVOR_EXTRA_DATA_ENABLED
-    os = clients.Manager()
-    images_client = os.images_client
-    flavors_client = os.flavors_client
-    extensions_client = os.extensions_client
-    DISK_CONFIG_ENABLED = (DISK_CONFIG_ENABLED_OVERRIDE and
-                           extensions_client.is_enabled('DiskConfig'))
-    FLAVOR_EXTRA_DATA_ENABLED = extensions_client.is_enabled('FlavorExtraData')
-
-    # Validate reference data exists
-    # If not, we raise the exception here and prevent
-    # going forward...
-    image_ref = CONFIG.compute.image_ref
-    image_ref_alt = CONFIG.compute.image_ref_alt
-    images_client.get_image(image_ref)
-    images_client.get_image(image_ref_alt)
-
-    flavor_ref = CONFIG.compute.flavor_ref
-    flavor_ref_alt = CONFIG.compute.flavor_ref_alt
-    flavors_client.get_flavor_details(flavor_ref)
-    flavors_client.get_flavor_details(flavor_ref_alt)
+    global MULTI_USER
 
     # Determine if there are two regular users that can be
     # used in testing. If the test cases are allowed to create
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index f201cf7..2eaf3b0 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -15,7 +15,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
+from tempest import config
 from tempest import exceptions
 from tempest.test import attr
 
@@ -51,6 +54,10 @@
 class FixedIPsTestJson(FixedIPsBase):
     _interface = 'json'
 
+    CONF = config.TempestConfig()
+
+    @testtools.skipIf(CONF.network.neutron_available, "This feature is not" +
+                      "implemented by Neutron. See bug: #1194569")
     @attr(type='gate')
     def test_list_fixed_ip_details(self):
         resp, fixed_ip = self.client.get_fixed_ip_details(self.ip)
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
new file mode 100644
index 0000000..a47e6c9
--- /dev/null
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -0,0 +1,76 @@
+# 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.compute import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class HostsAdminTestJSON(base.BaseComputeAdminTest):
+
+    """
+    Tests hosts API using admin privileges.
+    """
+
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(HostsAdminTestJSON, cls).setUpClass()
+        cls.client = cls.os_adm.hosts_client
+        cls.non_admin_client = cls.os.hosts_client
+
+    @attr(type=['positive', 'gate'])
+    def test_list_hosts(self):
+        resp, hosts = self.client.list_hosts()
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(hosts) >= 2)
+
+    @attr(type='positive')
+    def test_list_hosts_with_zone(self):
+        resp, hosts = self.client.list_hosts()
+        host = hosts[0]
+        zone_name = host['zone']
+        params = {'zone': zone_name}
+        resp, hosts = self.client.list_hosts(params)
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(hosts) >= 1)
+        self.assertTrue(host in hosts)
+
+    @attr(type='negative')
+    def test_list_hosts_with_non_existent_zone(self):
+        params = {'zone': 'xxx'}
+        resp, hosts = self.client.list_hosts(params)
+        self.assertEqual(0, len(hosts))
+        self.assertEqual(200, resp.status)
+
+    @attr(type='negative')
+    def test_list_hosts_with_a_blank_zone(self):
+        # If send the request with a blank zone, the request will be successful
+        # and it will return all the hosts list
+        params = {'zone': ''}
+        resp, hosts = self.client.list_hosts(params)
+        self.assertNotEqual(0, len(hosts))
+        self.assertEqual(200, resp.status)
+
+    @attr(type=['negative', 'gate'])
+    def test_list_hosts_with_non_admin_user(self):
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.list_hosts)
+
+
+class HostsAdminTestXML(HostsAdminTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
new file mode 100644
index 0000000..cb47066
--- /dev/null
+++ b/tempest/api/compute/admin/test_servers.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.compute import base
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+class ServersAdminTestJSON(base.BaseComputeAdminTest):
+
+    """
+    Tests Servers API using admin privileges
+    """
+
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(ServersAdminTestJSON, cls).setUpClass()
+        cls.client = cls.os_adm.servers_client
+
+        cls.s1_name = rand_name('server')
+        resp, server = cls.create_server(name=cls.s1_name,
+                                         wait_until='ACTIVE')
+        cls.s2_name = rand_name('server')
+        resp, server = cls.create_server(name=cls.s2_name,
+                                         wait_until='ACTIVE')
+
+    @attr(type='gate')
+    def test_list_servers_by_admin(self):
+        # Listing servers by admin user returns empty list by default
+        resp, body = self.client.list_servers_with_detail()
+        servers = body['servers']
+        self.assertEqual('200', resp['status'])
+        self.assertEqual([], servers)
+
+    @attr(type='gate')
+    def test_list_servers_by_admin_with_all_tenants(self):
+        # Listing servers by admin user with all tenants parameter
+        # Here should be listed all servers
+        params = {'all_tenants': ''}
+        resp, body = self.client.list_servers_with_detail(params)
+        servers = body['servers']
+        servers_name = map(lambda x: x['name'], servers)
+
+        self.assertIn(self.s1_name, servers_name)
+        self.assertIn(self.s2_name, servers_name)
+
+
+class ServersAdminTestXML(ServersAdminTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index f960ca4..e105121 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -15,8 +15,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
 from tempest.common.utils.data_utils import rand_name
+from tempest import config
 from tempest import exceptions
 from tempest.test import attr
 
@@ -155,6 +158,8 @@
                           self.client.create_security_group, s_name,
                           s_description)
 
+    @testtools.skipIf(config.TempestConfig().network.neutron_available,
+                      "Neutron allows duplicate names for security groups")
     @attr(type=['negative', 'gate'])
     def test_security_group_create_with_duplicate_name(self):
         # Negative test:Security Group with duplicate name should not
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 113ac78..de095c5 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -24,8 +24,8 @@
 
     @classmethod
     def setUpClass(cls):
-        if not cls.config.network.quantum_available:
-            raise cls.skipException("Quantum is required")
+        if not cls.config.network.neutron_available:
+            raise cls.skipException("Neutron is required")
         super(AttachInterfacesTestJSON, cls).setUpClass()
         cls.client = cls.os.interfaces_client
 
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 9f97f4f..304a512 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -253,6 +253,24 @@
         lines = len(output.split('\n'))
         self.assertEqual(lines, 10)
 
+    @attr(type='gate')
+    def test_pause_unpause_server(self):
+        resp, server = self.client.pause_server(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.client.wait_for_server_status(self.server_id, 'PAUSED')
+        resp, server = self.client.unpause_server(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+
+    @attr(type='gate')
+    def test_suspend_resume_server(self):
+        resp, server = self.client.suspend_server(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.client.wait_for_server_status(self.server_id, 'SUSPENDED')
+        resp, server = self.client.resume_server(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+
     @classmethod
     def rebuild_servers(cls):
         # Destroy any existing server and creates a new one
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index bbe489c..5cc8dc6 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -24,12 +24,12 @@
 from tempest.test import attr
 
 
-class ServersNegativeTest(base.BaseComputeTest):
+class ServersNegativeTestJSON(base.BaseComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(ServersNegativeTest, cls).setUpClass()
+        super(ServersNegativeTestJSON, cls).setUpClass()
         cls.client = cls.servers_client
         cls.img_client = cls.images_client
         cls.alt_os = clients.AltManager()
@@ -236,11 +236,7 @@
         # Create a server with a nonexistent security group
 
         security_groups = [{'name': 'does_not_exist'}]
-        if self.config.network.quantum_available:
-            expected_exception = exceptions.NotFound
-        else:
-            expected_exception = exceptions.BadRequest
-        self.assertRaises(expected_exception,
+        self.assertRaises(exceptions.BadRequest,
                           self.create_server,
                           security_groups=security_groups)
 
@@ -252,5 +248,5 @@
                           '999erra43')
 
 
-class ServersNegativeTestXML(ServersNegativeTest):
+class ServersNegativeTestXML(ServersNegativeTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 3119643..35f0fc0 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -16,9 +16,11 @@
 #    under the License.
 
 import netaddr
+import testtools
 
 from tempest.api.compute import base
 from tempest.common.utils.data_utils import rand_name
+from tempest import config
 from tempest import exceptions
 from tempest.test import attr
 
@@ -26,6 +28,8 @@
 class VirtualInterfacesTestJSON(base.BaseComputeTest):
     _interface = 'json'
 
+    CONF = config.TempestConfig()
+
     @classmethod
     def setUpClass(cls):
         super(VirtualInterfacesTestJSON, cls).setUpClass()
@@ -33,6 +37,8 @@
         resp, server = cls.create_server(wait_until='ACTIVE')
         cls.server_id = server['id']
 
+    @testtools.skipIf(CONF.network.neutron_available, "This feature is not " +
+                      "implemented by Neutron. See bug: #1183436")
     @attr(type='gate')
     def test_list_virtual_interfaces(self):
         # Positive test:Should be able to GET the virtual interfaces list
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index a6302e6..1acc57d 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -33,72 +33,68 @@
     def test_volume_create_get_delete(self):
         # CREATE, GET, DELETE Volume
         volume = None
-        try:
-            v_name = rand_name('Volume-%s-') % self._interface
-            metadata = {'Type': 'work'}
-            #Create volume
-            resp, volume = self.client.create_volume(size=1,
-                                                     display_name=v_name,
-                                                     metadata=metadata)
-            self.assertEqual(200, resp.status)
-            self.assertTrue('id' in volume)
-            self.assertTrue('displayName' in volume)
-            self.assertEqual(volume['displayName'], v_name,
-                             "The created volume name is not equal "
-                             "to the requested name")
-            self.assertTrue(volume['id'] is not None,
-                            "Field volume id is empty or not found.")
-            #Wait for Volume status to become ACTIVE
-            self.client.wait_for_volume_status(volume['id'], 'available')
-            #GET Volume
-            resp, fetched_volume = self.client.get_volume(volume['id'])
-            self.assertEqual(200, resp.status)
-            #Verfication of details of fetched Volume
-            self.assertEqual(v_name,
-                             fetched_volume['displayName'],
-                             'The fetched Volume is different '
-                             'from the created Volume')
-            self.assertEqual(volume['id'],
-                             fetched_volume['id'],
-                             'The fetched Volume is different '
-                             'from the created Volume')
-            self.assertEqual(metadata,
-                             fetched_volume['metadata'],
-                             'The fetched Volume is different '
-                             'from the created Volume')
-
-        finally:
-            if volume:
-                #Delete the Volume created in this method
-                resp, _ = self.client.delete_volume(volume['id'])
-                self.assertEqual(202, resp.status)
-                #Checking if the deleted Volume still exists
-                self.client.wait_for_resource_deletion(volume['id'])
+        v_name = rand_name('Volume-%s-') % self._interface
+        metadata = {'Type': 'work'}
+        #Create volume
+        resp, volume = self.client.create_volume(size=1,
+                                                 display_name=v_name,
+                                                 metadata=metadata)
+        self.addCleanup(self._delete_volume, volume)
+        self.assertEqual(200, resp.status)
+        self.assertTrue('id' in volume)
+        self.assertTrue('displayName' in volume)
+        self.assertEqual(volume['displayName'], v_name,
+                         "The created volume name is not equal "
+                         "to the requested name")
+        self.assertTrue(volume['id'] is not None,
+                        "Field volume id is empty or not found.")
+        #Wait for Volume status to become ACTIVE
+        self.client.wait_for_volume_status(volume['id'], 'available')
+        #GET Volume
+        resp, fetched_volume = self.client.get_volume(volume['id'])
+        self.assertEqual(200, resp.status)
+        #Verfication of details of fetched Volume
+        self.assertEqual(v_name,
+                         fetched_volume['displayName'],
+                         'The fetched Volume is different '
+                         'from the created Volume')
+        self.assertEqual(volume['id'],
+                         fetched_volume['id'],
+                         'The fetched Volume is different '
+                         'from the created Volume')
+        self.assertEqual(metadata,
+                         fetched_volume['metadata'],
+                         'The fetched Volume is different '
+                         'from the created Volume')
 
     @attr(type='gate')
     def test_volume_get_metadata_none(self):
         # CREATE, GET empty metadata dict
+        v_name = rand_name('Volume-')
+        #Create volume
+        resp, volume = self.client.create_volume(size=1,
+                                                 display_name=v_name,
+                                                 metadata={})
+        self.addCleanup(self._delete_volume, volume)
+        self.assertEqual(200, resp.status)
+        self.assertTrue('id' in volume)
+        self.assertTrue('displayName' in volume)
+        #Wait for Volume status to become ACTIVE
+        self.client.wait_for_volume_status(volume['id'], 'available')
+        #GET Volume
+        resp, fetched_volume = self.client.get_volume(volume['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(fetched_volume['metadata'], {})
+
+    def _delete_volume(self, volume):
+        #Delete the Volume created in this method
         try:
-            v_name = rand_name('Volume-')
-            #Create volume
-            resp, volume = self.client.create_volume(size=1,
-                                                     display_name=v_name,
-                                                     metadata={})
-            self.assertEqual(200, resp.status)
-            self.assertTrue('id' in volume)
-            self.assertTrue('displayName' in volume)
-            #Wait for Volume status to become ACTIVE
-            self.client.wait_for_volume_status(volume['id'], 'available')
-            #GET Volume
-            resp, fetched_volume = self.client.get_volume(volume['id'])
-            self.assertEqual(200, resp.status)
-            self.assertEqual(fetched_volume['metadata'], {})
-        finally:
-            #Delete the Volume created in this method
             resp, _ = self.client.delete_volume(volume['id'])
             self.assertEqual(202, resp.status)
             #Checking if the deleted Volume still exists
             self.client.wait_for_resource_deletion(volume['id'])
+        except KeyError:
+            return
 
 
 class VolumesGetTestXML(VolumesGetTestJSON):
diff --git a/tempest/api/identity/admin/test_users.py b/tempest/api/identity/admin/test_users.py
index c029300..0bba250 100644
--- a/tempest/api/identity/admin/test_users.py
+++ b/tempest/api/identity/admin/test_users.py
@@ -15,7 +15,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
 from testtools.matchers._basic import Contains
 
 from tempest.api.identity import base
@@ -77,32 +76,6 @@
                           self.data.test_user, self.data.test_password,
                           self.data.tenant['id'], self.data.test_email)
 
-    @testtools.skip("Until Bug #999084 is fixed")
-    @attr(type=['negative', 'gate'])
-    def test_create_user_with_empty_password(self):
-        # User with an empty password should not be created
-        self.data.setup_test_tenant()
-        self.assertRaises(exceptions.BadRequest, self.client.create_user,
-                          self.alt_user, '', self.data.tenant['id'],
-                          self.alt_email)
-
-    @testtools.skip("Until Bug #999084 is fixed")
-    @attr(type=['negative', 'gate'])
-    def test_create_user_with_long_password(self):
-        # User having password exceeding max length should not be created
-        self.data.setup_test_tenant()
-        self.assertRaises(exceptions.BadRequest, self.client.create_user,
-                          self.alt_user, 'a' * 65, self.data.tenant['id'],
-                          self.alt_email)
-
-    @testtools.skip("Until Bug #999084 is fixed")
-    @attr(type=['negative', 'gate'])
-    def test_create_user_with_invalid_email_format(self):
-        # Email format should be validated while creating a user
-        self.data.setup_test_tenant()
-        self.assertRaises(exceptions.BadRequest, self.client.create_user,
-                          self.alt_user, '', self.data.tenant['id'], '12345')
-
     @attr(type=['negative', 'gate'])
     def test_create_user_for_non_existant_tenant(self):
         # Attempt to create a user in a non-existent tenant should fail
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index 8d019fe..3d40eb3 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -15,6 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+
 from tempest.api.identity import base
 from tempest.common.utils.data_utils import rand_name
 from tempest.test import attr
@@ -49,6 +50,48 @@
         missing_doms = [d for d in domain_ids if d not in fetched_ids]
         self.assertEqual(0, len(missing_doms))
 
+    @attr(type='smoke')
+    def test_create_update_delete_domain(self):
+        d_name = rand_name('domain-')
+        d_desc = rand_name('domain-desc-')
+        resp_1, domain = self.v3_client.create_domain(
+            d_name, description=d_desc)
+        self.assertEqual(resp_1['status'], '201')
+        self.addCleanup(self._delete_domain, domain['id'])
+        self.assertIn('id', domain)
+        self.assertIn('description', domain)
+        self.assertIn('name', domain)
+        self.assertIn('enabled', domain)
+        self.assertIn('links', domain)
+        self.assertIsNotNone(domain['id'])
+        self.assertEqual(d_name, domain['name'])
+        self.assertEqual(d_desc, domain['description'])
+        if self._interface == "json":
+            self.assertEqual(True, domain['enabled'])
+        else:
+            self.assertEqual('true', str(domain['enabled']).lower())
+        new_desc = rand_name('new-desc-')
+        new_name = rand_name('new-name-')
+
+        resp_2, updated_domain = self.v3_client.update_domain(
+            domain['id'], name=new_name, description=new_desc)
+        self.assertEqual(resp_2['status'], '200')
+        self.assertIn('id', updated_domain)
+        self.assertIn('description', updated_domain)
+        self.assertIn('name', updated_domain)
+        self.assertIn('enabled', updated_domain)
+        self.assertIn('links', updated_domain)
+        self.assertIsNotNone(updated_domain['id'])
+        self.assertEqual(new_name, updated_domain['name'])
+        self.assertEqual(new_desc, updated_domain['description'])
+        self.assertEqual('true', str(updated_domain['enabled']).lower())
+
+        resp_3, fetched_domain = self.v3_client.get_domain(domain['id'])
+        self.assertEqual(resp_3['status'], '200')
+        self.assertEqual(new_name, fetched_domain['name'])
+        self.assertEqual(new_desc, fetched_domain['description'])
+        self.assertEqual('true', str(fetched_domain['enabled']).lower())
+
 
 class DomainsTestXML(DomainsTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 8068284..3b7f9dd 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -26,11 +26,11 @@
 class BaseNetworkTest(tempest.test.BaseTestCase):
 
     """
-    Base class for the Quantum tests that use the Tempest Quantum REST client
+    Base class for the Neutron tests that use the Tempest Neutron REST client
 
-    Per the Quantum API Guide, API v1.x was removed from the source code tree
+    Per the Neutron API Guide, API v1.x was removed from the source code tree
     (docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html)
-    Therefore, v2.x of the Quantum API is assumed. It is also assumed that the
+    Therefore, v2.x of the Neutron API is assumed. It is also assumed that the
     following options are defined in the [network] section of etc/tempest.conf:
 
         tenant_network_cidr with a block of cidr's from which smaller blocks
@@ -44,8 +44,8 @@
     def setUpClass(cls):
         os = clients.Manager()
         cls.network_cfg = os.config.network
-        if not cls.network_cfg.quantum_available:
-            raise cls.skipException("Quantum support is required")
+        if not cls.network_cfg.neutron_available:
+            raise cls.skipException("Neutron support is required")
         cls.client = os.network_client
         cls.networks = []
         cls.subnets = []
@@ -73,6 +73,7 @@
         cidr = netaddr.IPNetwork(cls.network_cfg.tenant_network_cidr)
         mask_bits = cls.network_cfg.tenant_network_mask_bits
         # Find a cidr that is not in use yet and create a subnet with it
+        body = None
         failure = None
         for subnet_cidr in cidr.subnet(mask_bits):
             try:
diff --git a/tempest/api/network/common.py b/tempest/api/network/common.py
index 22eb1d3..c3fb821 100644
--- a/tempest/api/network/common.py
+++ b/tempest/api/network/common.py
@@ -32,7 +32,7 @@
 class DeletableResource(AttributeDict):
 
     """
-    Support deletion of quantum resources (networks, subnets) via a
+    Support deletion of neutron resources (networks, subnets) via a
     delete() method, as is supported by keystone and nova resources.
     """
 
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 1f45f92..4481853 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -26,8 +26,8 @@
 class NetworksTest(base.BaseNetworkTest):
 
     """
-    Tests the following operations in the Quantum API using the REST client for
-    Quantum:
+    Tests the following operations in the Neutron API using the REST client for
+    Neutron:
 
         create a network for a tenant
         list tenant's networks
@@ -36,7 +36,7 @@
         list tenant's subnets
         show a tenant subnet details
 
-    v2.0 of the Quantum API is assumed. It is also assumed that the following
+    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:
 
         tenant_network_cidr with a block of cidr's from which smaller blocks
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index b40774e..029f2d5 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -19,6 +19,7 @@
 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 AccountTest(base.BaseObjectTest):
@@ -47,7 +48,7 @@
     def test_list_account_metadata(self):
         # list all account metadata
         resp, metadata = self.account_client.list_account_metadata()
-        self.assertEqual(resp['status'], '204')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
         self.assertIn('x-account-object-count', resp)
         self.assertIn('x-account-container-count', resp)
         self.assertIn('x-account-bytes-used', resp)
@@ -59,7 +60,7 @@
         # add metadata to account
         resp, _ = self.account_client.create_account_metadata(
             metadata={header: data})
-        self.assertEqual(resp['status'], '204')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
 
         resp, _ = self.account_client.list_account_metadata()
         self.assertIn('x-account-meta-' + header, resp)
@@ -68,7 +69,7 @@
         # delete metadata from account
         resp, _ = \
             self.account_client.delete_account_metadata(metadata=[header])
-        self.assertEqual(resp['status'], '204')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
 
         resp, _ = self.account_client.list_account_metadata()
         self.assertNotIn('x-account-meta-' + header, resp)
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 70f704f..5cb6341 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -19,6 +19,7 @@
 from tempest.common.utils.data_utils import arbitrary_string
 from tempest.common.utils.data_utils import rand_name
 from tempest.test import attr
+from tempest.test import HTTP_SUCCESS
 
 
 class ContainerTest(base.BaseObjectTest):
@@ -46,7 +47,7 @@
         self.containers.append(container_name)
         # delete container
         resp, _ = self.container_client.delete_container(container_name)
-        self.assertEqual(resp['status'], '204')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
         self.containers.remove(container_name)
 
     @attr(type='smoke')
@@ -74,7 +75,7 @@
         resp, object_list = \
             self.container_client.\
             list_container_contents(container_name, params=params)
-        self.assertEqual(resp['status'], '200')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
         self.assertIsNotNone(object_list)
 
         object_names = [obj['name'] for obj in object_list]
@@ -95,12 +96,12 @@
         resp, _ = \
             self.container_client.update_container_metadata(container_name,
                                                             metadata=metadata)
-        self.assertEqual(resp['status'], '204')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
 
         # list container metadata
         resp, _ = self.container_client.list_container_metadata(
             container_name)
-        self.assertEqual(resp['status'], '204')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
         self.assertIn('x-container-meta-name', resp)
         self.assertIn('x-container-meta-description', resp)
         self.assertEqual(resp['x-container-meta-name'], 'Pictures')
@@ -110,10 +111,10 @@
         resp, _ = self.container_client.delete_container_metadata(
             container_name,
             metadata=metadata.keys())
-        self.assertEqual(resp['status'], '204')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
 
         # check if the metadata are no longer there
         resp, _ = self.container_client.list_container_metadata(container_name)
-        self.assertEqual(resp['status'], '204')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
         self.assertNotIn('x-container-meta-name', resp)
         self.assertNotIn('x-container-meta-description', resp)
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index a83e92c..6136216 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -23,6 +23,7 @@
 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 ObjectTest(base.BaseObjectTest):
@@ -72,7 +73,7 @@
         # delete object
         resp, _ = self.object_client.delete_object(self.container_name,
                                                    object_name)
-        self.assertEqual(resp['status'], '204')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
 
     @attr(type='smoke')
     def test_object_metadata(self):
@@ -89,12 +90,12 @@
         orig_metadata = {meta_key: meta_value}
         resp, _ = self.object_client.update_object_metadata(
             self.container_name, object_name, orig_metadata)
-        self.assertEqual(resp['status'], '202')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
 
         # get object metadata
         resp, resp_metadata = self.object_client.list_object_metadata(
             self.container_name, object_name)
-        self.assertEqual(resp['status'], '200')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
         actual_meta_key = 'x-object-meta-' + meta_key
         self.assertTrue(actual_meta_key in resp)
         self.assertEqual(resp[actual_meta_key], meta_value)
@@ -111,7 +112,7 @@
         # get object
         resp, body = self.object_client.get_object(self.container_name,
                                                    object_name)
-        self.assertEqual(resp['status'], '200')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
         self.assertEqual(body, data)
 
     @attr(type='smoke')
@@ -209,7 +210,7 @@
         resp, _ = self.object_client.update_object_metadata(src_container_name,
                                                             object_name,
                                                             orig_metadata)
-        self.assertEqual(resp['status'], '202')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
         try:
             # copy object from source container to destination container
             resp, _ = self.object_client.copy_object_across_containers(
@@ -309,7 +310,7 @@
             metadata = {'Temp-URL-Key': key}
             resp, _ = self.account_client.create_account_metadata(
                 metadata=metadata)
-            self.assertEqual(resp['status'], '204')
+            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)
@@ -339,18 +340,19 @@
     def test_object_upload_in_segments(self):
         # create object
         object_name = rand_name(name='LObject')
-        data = arbitrary_string(size=len(object_name),
-                                base_text=object_name)
+        data = arbitrary_string()
         segments = 10
-        self.object_client.create_object(self.container_name,
-                                         object_name, data)
-        # uploading 10 segments
-        for i in range(segments):
+        data_segments = [data + str(i) for i in xrange(segments)]
+        # uploading segments
+        for i in xrange(segments):
             resp, _ = self.object_client.create_object_segments(
-                self.container_name, object_name, i, data)
-        # creating a manifest file (metadata update)
+                self.container_name, object_name, i, data_segments[i])
+            self.assertEqual(resp['status'], '201')
+        # creating a manifest file
         metadata = {'X-Object-Manifest': '%s/%s/'
                     % (self.container_name, object_name)}
+        self.object_client.create_object(self.container_name,
+                                         object_name, data='')
         resp, _ = self.object_client.update_object_metadata(
             self.container_name, object_name, metadata, metadata_prefix='')
         resp, _ = self.object_client.list_object_metadata(
@@ -362,7 +364,7 @@
         # downloading the object
         resp, body = self.object_client.get_object(
             self.container_name, object_name)
-        self.assertEqual(data * segments, body)
+        self.assertEqual(''.join(data_segments), body)
 
     @attr(type='gate')
     def test_get_object_if_different(self):
@@ -386,7 +388,7 @@
         md5 = hashlib.md5(local_data).hexdigest()
         headers = {'If-None-Match': md5}
         resp, body = self.object_client.get(url, headers=headers)
-        self.assertEqual(resp['status'], '200')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
 
 
 class PublicObjectTest(base.BaseObjectTest):
@@ -408,7 +410,7 @@
         cont_headers = {'X-Container-Read': '.r:*,.rlistings'}
         resp_meta, body = self.container_client.update_container_metadata(
             self.container_name, metadata=cont_headers, metadata_prefix='')
-        self.assertEqual(resp_meta['status'], '204')
+        self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
         # create object
         object_name = rand_name(name='Object')
         data = arbitrary_string(size=len(object_name),
@@ -420,7 +422,7 @@
         # list container metadata
         resp_meta, _ = self.container_client.list_container_metadata(
             self.container_name)
-        self.assertEqual(resp_meta['status'], '204')
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
         self.assertIn('x-container-read', resp_meta)
         self.assertEqual(resp_meta['x-container-read'], '.r:*,.rlistings')
 
@@ -438,7 +440,7 @@
             resp_meta, body = self.container_client.update_container_metadata(
                 self.container_name, metadata=cont_headers,
                 metadata_prefix='')
-            self.assertEqual(resp_meta['status'], '204')
+            self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
             # create object
             object_name = rand_name(name='Object')
             data = arbitrary_string(size=len(object_name) * 1,
@@ -450,7 +452,7 @@
             # list container metadata
             resp, _ = self.container_client.list_container_metadata(
                 self.container_name)
-            self.assertEqual(resp['status'], '204')
+            self.assertIn(int(resp['status']), HTTP_SUCCESS)
             self.assertIn('x-container-read', resp)
             self.assertEqual(resp['x-container-read'], '.r:*,.rlistings')
 
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 544558e..ffa534a 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
+from tempest.common import log as logging
 import time
 
 from tempest import clients
@@ -33,11 +33,15 @@
         cls.orchestration_cfg = os.config.orchestration
         if not cls.orchestration_cfg.heat_available:
             raise cls.skipException("Heat support is required")
+        cls.build_timeout = cls.orchestration_cfg.build_timeout
+        cls.build_interval = cls.orchestration_cfg.build_interval
 
         cls.os = os
         cls.orchestration_client = os.orchestration_client
+        cls.servers_client = os.servers_client
         cls.keypairs_client = os.keypairs_client
         cls.stacks = []
+        cls.keypairs = []
 
     @classmethod
     def _get_identity_admin_client(cls):
@@ -58,15 +62,15 @@
             cls.config.identity.uri
         )
 
-    def create_stack(self, stack_name, template_data, parameters={}):
-        resp, body = self.client.create_stack(
+    @classmethod
+    def create_stack(cls, stack_name, template_data, parameters={}):
+        resp, body = cls.client.create_stack(
             stack_name,
             template=template_data,
             parameters=parameters)
-        self.assertEqual('201', resp['status'])
         stack_id = resp['location'].split('/')[-1]
         stack_identifier = '%s/%s' % (stack_name, stack_id)
-        self.stacks.append(stack_identifier)
+        cls.stacks.append(stack_identifier)
         return stack_identifier
 
     @classmethod
@@ -84,15 +88,25 @@
             except Exception:
                 pass
 
-    def _create_keypair(self, namestart='keypair-heat-'):
+    @classmethod
+    def _create_keypair(cls, namestart='keypair-heat-'):
         kp_name = rand_name(namestart)
-        resp, body = self.keypairs_client.create_keypair(kp_name)
-        self.assertEqual(body['name'], kp_name)
+        resp, body = cls.keypairs_client.create_keypair(kp_name)
+        cls.keypairs.append(kp_name)
         return body
 
     @classmethod
+    def clear_keypairs(cls):
+        for kp_name in cls.keypairs:
+            try:
+                cls.keypairs_client.delete_keypair(kp_name)
+            except Exception:
+                pass
+
+    @classmethod
     def tearDownClass(cls):
         cls.clear_stacks()
+        cls.clear_keypairs()
 
     def wait_for(self, condition):
         """Repeatedly calls condition() until a timeout."""
@@ -108,3 +122,9 @@
                 condition()
                 return
             time.sleep(self.build_interval)
+
+    @staticmethod
+    def stack_output(stack, output_key):
+        """Return a stack output value for a give key."""
+        return next((o['output_value'] for o in stack['outputs']
+                    if o['output_key'] == output_key), None)
diff --git a/tempest/api/orchestration/stacks/test_instance_cfn_init.py b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
index 2349830..16509ea 100644
--- a/tempest/api/orchestration/stacks/test_instance_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
@@ -13,10 +13,13 @@
 #    under the License.
 
 import json
-import logging
+from tempest.common import log as logging
+import testtools
 
 from tempest.api.orchestration import base
 from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils.linux.remote_client import RemoteClient
+import tempest.config
 from tempest.test import attr
 
 
@@ -25,6 +28,8 @@
 
 class InstanceCfnInitTestJSON(base.BaseOrchestrationTest):
     _interface = 'json'
+    existing_keypair = (tempest.config.TempestConfig().
+                        orchestration.keypair_name is not None)
 
     template = """
 HeatTemplateFormatVersion: '2012-12-12'
@@ -101,6 +106,10 @@
     Description: Contents of /tmp/smoke-status on SmokeServer
     Value:
       Fn::GetAtt: [WaitCondition, Data]
+  SmokeServerIp:
+    Description: IP address of server
+    Value:
+      Fn::GetAtt: [SmokeServer, PublicIp]
 """
 
     @classmethod
@@ -110,23 +119,47 @@
             raise cls.skipException("No image available to test")
         cls.client = cls.orchestration_client
 
-    def setUp(self):
-        super(InstanceCfnInitTestJSON, self).setUp()
         stack_name = rand_name('heat')
-        keypair_name = (self.orchestration_cfg.keypair_name or
-                        self._create_keypair()['name'])
+        if cls.orchestration_cfg.keypair_name:
+            keypair_name = cls.orchestration_cfg.keypair_name
+        else:
+            cls.keypair = cls._create_keypair()
+            keypair_name = cls.keypair['name']
 
         # create the stack
-        self.stack_identifier = self.create_stack(
+        cls.stack_identifier = cls.create_stack(
             stack_name,
-            self.template,
+            cls.template,
             parameters={
                 'KeyName': keypair_name,
-                'InstanceType': self.orchestration_cfg.instance_type,
-                'ImageId': self.orchestration_cfg.image_ref
+                'InstanceType': cls.orchestration_cfg.instance_type,
+                'ImageId': cls.orchestration_cfg.image_ref
             })
 
     @attr(type='gate')
+    @testtools.skipIf(existing_keypair, 'Server ssh tests are disabled.')
+    def test_can_log_into_created_server(self):
+
+        sid = self.stack_identifier
+        rid = 'SmokeServer'
+
+        # wait for server resource create to complete.
+        self.client.wait_for_resource_status(sid, rid, 'CREATE_COMPLETE')
+
+        resp, body = self.client.get_resource(sid, rid)
+        self.assertEqual('CREATE_COMPLETE', body['resource_status'])
+
+        # fetch the ip address from servers client, since we can't get it
+        # from the stack until stack create is complete
+        resp, server = self.servers_client.get_server(
+            body['physical_resource_id'])
+
+        # Check that the user can authenticate with the generated password
+        linux_client = RemoteClient(
+            server, 'ec2-user', pkey=self.keypair['private_key'])
+        self.assertTrue(linux_client.can_authenticate())
+
+    @attr(type='gate')
     def test_stack_wait_condition_data(self):
 
         sid = self.stack_identifier
@@ -148,5 +181,6 @@
         # - a user was created and credentials written to the instance
         # - a cfn-signal was built which was signed with provided credentials
         # - the wait condition was fulfilled and the stack has changed state
-        wait_status = json.loads(body['outputs'][0]['output_value'])
+        wait_status = json.loads(
+            self.stack_output(body, 'WaitConditionStatus'))
         self.assertEqual('smoke test complete', wait_status['00000'])
diff --git a/tempest/api/orchestration/stacks/test_stacks.py b/tempest/api/orchestration/stacks/test_stacks.py
index 5fed581..15979ed 100644
--- a/tempest/api/orchestration/stacks/test_stacks.py
+++ b/tempest/api/orchestration/stacks/test_stacks.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
+from tempest.common import log as logging
 
 from tempest.api.orchestration import base
 from tempest.common.utils.data_utils import rand_name
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 4131d3e..3c4b5d8 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -55,8 +55,10 @@
             volume = {}
             vol_name = rand_name("volume-")
             vol_type_name = rand_name("volume-type-")
-            extra_specs = {"storage_protocol": "iSCSI",
-                           "vendor_name": "Open Source"}
+            proto = self.config.volume.storage_protocol
+            vendor = self.config.volume.vendor_name
+            extra_specs = {"storage_protocol": proto,
+                           "vendor_name": vendor}
             body = {}
             resp, body = self.client.create_volume_type(
                 vol_type_name,
diff --git a/tempest/cli/README.rst b/tempest/cli/README.rst
index 76b05a3..3eae492 100644
--- a/tempest/cli/README.rst
+++ b/tempest/cli/README.rst
@@ -36,7 +36,7 @@
 If a test is validating the cli for bad data, it should do it with
 assertRaises.
 
-A reasonable example of an existing test is as follows:
+A reasonable example of an existing test is as follows::
 
     def test_admin_list(self):
         self.nova('list')
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index 413990d..0e1d6db 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -15,13 +15,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
+import os
 import shlex
 import subprocess
 
 from oslo.config import cfg
 
 import tempest.cli.output_parser
+from tempest.common import log as logging
 import tempest.test
 
 
@@ -76,6 +77,11 @@
         return self.cmd_with_auth(
             'glance', action, flags, params, admin, fail_ok)
 
+    def cinder(self, action, flags='', params='', admin=True, fail_ok=False):
+        """Executes cinder command for the given action."""
+        return self.cmd_with_auth(
+            'cinder', action, flags, params, admin, fail_ok)
+
     def cmd_with_auth(self, cmd, action, flags='', params='',
                       admin=True, fail_ok=False):
         """Executes given command with auth attributes appended."""
@@ -99,7 +105,7 @@
     def cmd(self, cmd, action, flags='', params='', fail_ok=False,
             merge_stderr=False):
         """Executes specified command for the given action."""
-        cmd = ' '.join([CONF.cli.cli_dir + cmd,
+        cmd = ' '.join([os.path.join(CONF.cli.cli_dir, cmd),
                         flags, action, params])
         LOG.info("running: '%s'" % cmd)
         cmd = shlex.split(cmd)
@@ -109,7 +115,7 @@
             else:
                 with open('/dev/null', 'w') as devnull:
                     result = self.check_output(cmd, stderr=devnull)
-        except subprocess.CalledProcessError, e:
+        except subprocess.CalledProcessError as e:
             LOG.error("command output:\n%s" % e.output)
             raise
         return result
diff --git a/tempest/cli/output_parser.py b/tempest/cli/output_parser.py
index 840839b..3ee3098 100644
--- a/tempest/cli/output_parser.py
+++ b/tempest/cli/output_parser.py
@@ -18,7 +18,8 @@
 """Collection of utilities for parsing CLI clients output."""
 
 
-import logging
+from tempest.common import log as logging
+
 import re
 
 
diff --git a/tempest/cli/simple_read_only/test_cinder.py b/tempest/cli/simple_read_only/test_cinder.py
new file mode 100644
index 0000000..e9ce87b
--- /dev/null
+++ b/tempest/cli/simple_read_only/test_cinder.py
@@ -0,0 +1,117 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
+
+import logging
+import re
+import subprocess
+
+import tempest.cli
+
+LOG = logging.getLogger(__name__)
+
+
+class SimpleReadOnlyCinderClientTest(tempest.cli.ClientTestBase):
+    """Basic, read-only tests for Cinder CLI client.
+
+    Checks return values and output of read-only commands.
+    These tests do not presume any content, nor do they create
+    their own. They only verify the structure of output if present.
+    """
+
+    def test_cinder_fake_action(self):
+        self.assertRaises(subprocess.CalledProcessError,
+                          self.cinder,
+                          'this-does-not-exist')
+
+    def test_cinder_absolute_limit_list(self):
+        roles = self.parser.listing(self.cinder('absolute-limits'))
+        self.assertTableStruct(roles, ['Name', 'Value'])
+
+    def test_cinder_backup_list(self):
+        self.cinder('backup-list')
+
+    def test_cinder_extra_specs_list(self):
+        self.cinder('extra-specs-list')
+
+    def test_cinder_volumes_list(self):
+        self.cinder('list')
+
+    def test_cinder_quota_class_show(self):
+        """This CLI can accept and string as param."""
+        roles = self.parser.listing(self.cinder('quota-class-show',
+                                                params='abc'))
+        self.assertTableStruct(roles, ['Property', 'Value'])
+
+    def test_cinder_quota_defaults(self):
+        """This CLI can accept and string as param."""
+        roles = self.parser.listing(self.cinder('quota-defaults',
+                                                params=self.identity.
+                                                admin_tenant_name))
+        self.assertTableStruct(roles, ['Property', 'Value'])
+
+    def test_cinder_quota_show(self):
+        """This CLI can accept and string as param."""
+        roles = self.parser.listing(self.cinder('quota-show',
+                                                params=self.identity.
+                                                admin_tenant_name))
+        self.assertTableStruct(roles, ['Property', 'Value'])
+
+    def test_cinder_rate_limits(self):
+        self.cinder('rate-limits')
+
+    def test_cinder_snapshot_list(self):
+        self.cinder('snapshot-list')
+
+    def test_cinder_type_list(self):
+        self.cinder('type-list')
+
+    def test_cinder_list_extensions(self):
+        self.cinder('list-extensions')
+        roles = self.parser.listing(self.cinder('list-extensions'))
+        self.assertTableStruct(roles, ['Name', 'Summary', 'Alias', 'Updated'])
+
+    def test_admin_help(self):
+        help_text = self.cinder('help')
+        lines = help_text.split('\n')
+        self.assertTrue(lines[0].startswith('usage: cinder'))
+
+        commands = []
+        cmds_start = lines.index('Positional arguments:')
+        cmds_end = lines.index('Optional arguments:')
+        command_pattern = re.compile('^ {4}([a-z0-9\-\_]+)')
+        for line in lines[cmds_start:cmds_end]:
+            match = command_pattern.match(line)
+            if match:
+                commands.append(match.group(1))
+        commands = set(commands)
+        wanted_commands = set(('absolute-limits', 'list', 'help',
+                               'quota-show', 'type-list', 'snapshot-list'))
+        self.assertFalse(wanted_commands - commands)
+
+     # Optional arguments:
+
+    def test_cinder_version(self):
+        self.cinder('', flags='--version')
+
+    def test_cinder_debug_list(self):
+        self.cinder('list', flags='--debug')
+
+    def test_cinder_retries_list(self):
+        self.cinder('list', flags='--retries 3')
+
+    def test_cinder_region_list(self):
+        self.cinder('list', flags='--os-region-name ' + self.identity.region)
diff --git a/tempest/cli/simple_read_only/test_compute.py b/tempest/cli/simple_read_only/test_compute.py
index fa64561..561fd00 100644
--- a/tempest/cli/simple_read_only/test_compute.py
+++ b/tempest/cli/simple_read_only/test_compute.py
@@ -15,13 +15,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import subprocess
 
 from oslo.config import cfg
 import testtools
 
 import tempest.cli
+from tempest.common import log as logging
 
 
 CONF = cfg.CONF
diff --git a/tempest/cli/simple_read_only/test_compute_manage.py b/tempest/cli/simple_read_only/test_compute_manage.py
index a788c8b..802a206 100644
--- a/tempest/cli/simple_read_only/test_compute_manage.py
+++ b/tempest/cli/simple_read_only/test_compute_manage.py
@@ -15,10 +15,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import subprocess
 
 import tempest.cli
+from tempest.common import log as logging
 
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/simple_read_only/test_glance.py b/tempest/cli/simple_read_only/test_glance.py
index b3b3eb7..fa77e8a 100644
--- a/tempest/cli/simple_read_only/test_glance.py
+++ b/tempest/cli/simple_read_only/test_glance.py
@@ -15,11 +15,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import re
 import subprocess
 
 import tempest.cli
+from tempest.common import log as logging
 
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/simple_read_only/test_keystone.py b/tempest/cli/simple_read_only/test_keystone.py
index 45d519b..3bc8b3e 100644
--- a/tempest/cli/simple_read_only/test_keystone.py
+++ b/tempest/cli/simple_read_only/test_keystone.py
@@ -15,11 +15,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import re
 import subprocess
 
 import tempest.cli
+from tempest.common import log as logging
 
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/clients.py b/tempest/clients.py
index a5c7b4d..d7a740a 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -399,5 +399,5 @@
         base = super(OrchestrationManager, self)
         base.__init__(conf.identity.admin_username,
                       conf.identity.admin_password,
-                      conf.identity.admin_tenant_name,
+                      conf.identity.tenant_name,
                       interface=interface)
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index d19d216..cd33a22 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -304,14 +304,14 @@
         if self.cert_file:
             try:
                 self.context.use_certificate_file(self.cert_file)
-            except Exception, e:
+            except Exception as e:
                 msg = 'Unable to load cert from "%s" %s' % (self.cert_file, e)
                 raise exc.SSLConfigurationError(msg)
             if self.key_file is None:
                 # We support having key and cert in same file
                 try:
                     self.context.use_privatekey_file(self.cert_file)
-                except Exception, e:
+                except Exception as e:
                     msg = ('No key file specified and unable to load key '
                            'from "%s" %s' % (self.cert_file, e))
                     raise exc.SSLConfigurationError(msg)
@@ -319,14 +319,14 @@
         if self.key_file:
             try:
                 self.context.use_privatekey_file(self.key_file)
-            except Exception, e:
+            except Exception as e:
                 msg = 'Unable to load key from "%s" %s' % (self.key_file, e)
                 raise exc.SSLConfigurationError(msg)
 
         if self.cacert:
             try:
                 self.context.load_verify_locations(self.cacert)
-            except Exception, e:
+            except Exception as e:
                 msg = 'Unable to load CA from "%s"' % (self.cacert, e)
                 raise exc.SSLConfigurationError(msg)
         else:
diff --git a/tempest/common/log.py b/tempest/common/log.py
index 9b35723..2159bfe 100644
--- a/tempest/common/log.py
+++ b/tempest/common/log.py
@@ -55,7 +55,7 @@
     log_config = os.path.join(conf_dir, conf_file)
     try:
         logging.config.fileConfig(log_config)
-    except ConfigParser.Error, exc:
+    except ConfigParser.Error as exc:
         raise cfg.ConfigFileParseError(log_config, str(exc))
     return True
 
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 531dfc8..e94455d 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -144,8 +144,8 @@
             try:
                 auth_data = json.loads(resp_body)['access']
                 token = auth_data['token']['id']
-            except Exception, e:
-                print "Failed to obtain token for user: %s" % e
+            except Exception as e:
+                print("Failed to obtain token for user: %s" % e)
                 raise
 
             mgmt_url = None
diff --git a/tempest/config.py b/tempest/config.py
index 7852eba..96b144c 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -15,9 +15,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from __future__ import print_function
+
 import os
 import sys
 
+
 from oslo.config import cfg
 
 from tempest.common import log as logging
@@ -181,10 +184,12 @@
                default=None,
                help="Path to a private key file for SSH access to remote "
                     "hosts"),
-    cfg.BoolOpt('disk_config_enabled_override',
+    cfg.BoolOpt('disk_config_enabled',
                 default=True,
-                help="If false, skip config tests regardless of the "
-                     "extension status"),
+                help="If false, skip disk config tests"),
+    cfg.BoolOpt('flavor_extra_enabled',
+                default=True,
+                help="If false, skip flavor extra data test"),
 ]
 
 
@@ -275,7 +280,7 @@
 NetworkGroup = [
     cfg.StrOpt('catalog_type',
                default='network',
-               help='Catalog type of the Quantum service.'),
+               help='Catalog type of the Neutron service.'),
     cfg.StrOpt('tenant_network_cidr',
                default="10.100.0.0/16",
                help="The cidr block to allocate tenant networks from"),
@@ -294,9 +299,9 @@
                default="",
                help="Id of the public router that provides external "
                     "connectivity"),
-    cfg.BoolOpt('quantum_available',
+    cfg.BoolOpt('neutron_available',
                 default=False,
-                help="Whether or not quantum is expected to be available"),
+                help="Whether or not neutron is expected to be available"),
 ]
 
 
@@ -328,6 +333,12 @@
     cfg.StrOpt('backend2_name',
                default='BACKEND_2',
                help="Name of the backend2 (must be declared in cinder.conf)"),
+    cfg.StrOpt('storage_protocol',
+               default='iSCSI',
+               help='Backend protocol to target when creating volume types'),
+    cfg.StrOpt('vendor_name',
+               default='Open Source',
+               help='Backend vendor to target when creating volume types'),
 ]
 
 
@@ -551,7 +562,7 @@
 
         if not os.path.exists(path):
             msg = "Config file %(path)s not found" % locals()
-            print >> sys.stderr, RuntimeError(msg)
+            print(RuntimeError(msg), file=sys.stderr)
         else:
             config_files.append(path)
 
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 5e941da..f9eb968 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -17,7 +17,7 @@
 import re
 
 
-PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'quantum']
+PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron']
 
 SKIP_DECORATOR_RE = re.compile(r'\s*@testtools.skip\((.*)\)')
 SKIP_STR_RE = re.compile(r'.*Bug #\d+.*')
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 366ff43..fe6fbf5 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -16,7 +16,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import subprocess
 
 # Default client libs
@@ -24,12 +23,13 @@
 import glanceclient
 import keystoneclient.v2_0.client
 import netaddr
+from neutronclient.common import exceptions as exc
+import neutronclient.v2_0.client
 import novaclient.client
-from quantumclient.common import exceptions as exc
-import quantumclient.v2_0.client
 
 
 from tempest.api.network import common as net_common
+from tempest.common import log as logging
 from tempest.common import ssh
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
@@ -166,7 +166,7 @@
         auth_url = self.config.identity.uri
         dscv = self.config.identity.disable_ssl_certificate_validation
 
-        return quantumclient.v2_0.client.Client(username=username,
+        return neutronclient.v2_0.client.Client(username=username,
                                                 password=password,
                                                 tenant_name=tenant_name,
                                                 auth_url=auth_url,
@@ -236,9 +236,9 @@
 
     @classmethod
     def check_preconditions(cls):
-        if (cls.config.network.quantum_available):
+        if (cls.config.network.neutron_available):
             cls.enabled = True
-            #verify that quantum_available is telling the truth
+            #verify that neutron_available is telling the truth
             try:
                 cls.network_client.list_networks()
             except exc.EndpointNotFound:
@@ -246,7 +246,7 @@
                 raise
         else:
             cls.enabled = False
-            msg = 'Quantum not available'
+            msg = 'Neutron not available'
             raise cls.skipException(msg)
 
     @classmethod
@@ -358,7 +358,7 @@
             try:
                 result = self.network_client.create_subnet(body=body)
                 break
-            except exc.QuantumClientException as e:
+            except exc.NeutronClientException as e:
                 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
                 if not is_overlapping_cidr:
                     raise
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index a55bbb2..2097f50 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -15,7 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
+from tempest.common import log as logging
 
 from tempest.common.utils.data_utils import rand_name
 from tempest.common.utils.linux.remote_client import RemoteClient
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index b94caaa..390e004 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -26,7 +26,7 @@
 
     """
     This smoke test suite assumes that Nova has been configured to
-    boot VM's with Quantum-managed networking, and attempts to
+    boot VM's with Neutron-managed networking, and attempts to
     verify network connectivity as follows:
 
      * For a freshly-booted VM with an IP address ("port") on a given network:
diff --git a/tempest/scenario/test_network_quotas.py b/tempest/scenario/test_network_quotas.py
index 8c3af73..267aff6 100644
--- a/tempest/scenario/test_network_quotas.py
+++ b/tempest/scenario/test_network_quotas.py
@@ -15,7 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from quantumclient.common import exceptions as exc
+from neutronclient.common import exceptions as exc
 from tempest.scenario.manager import NetworkScenarioTest
 
 MAX_REASONABLE_ITERATIONS = 51  # more than enough. Default for port is 50.
@@ -48,7 +48,7 @@
                 self.networks.append(
                     self._create_network(self.tenant_id,
                                          namestart='network-quotatest-'))
-            except exc.QuantumClientException as e:
+            except exc.NeutronClientException as e:
                 if (e.status_code != 409):
                     raise
                 hit_limit = True
@@ -66,7 +66,7 @@
                 self.subnets.append(
                     self._create_subnet(self.networks[0],
                                         namestart='subnet-quotatest-'))
-            except exc.QuantumClientException as e:
+            except exc.NeutronClientException as e:
                 if (e.status_code != 409):
                     raise
                 hit_limit = True
@@ -84,7 +84,7 @@
                 self.ports.append(
                     self._create_port(self.networks[0],
                                       namestart='port-quotatest-'))
-            except exc.QuantumClientException as e:
+            except exc.NeutronClientException as e:
                 if (e.status_code != 409):
                     raise
                 hit_limit = True
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
new file mode 100644
index 0000000..f21a00b
--- /dev/null
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -0,0 +1,134 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC Corporation
+# 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 import log as logging
+
+from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils.linux.remote_client import RemoteClient
+from tempest.scenario import manager
+
+
+LOG = logging.getLogger(__name__)
+
+
+class TestSnapshotPattern(manager.OfficialClientTest):
+    """
+    This test is for snapshotting an instance and booting with it.
+    The following is the scenario outline:
+     * boot a instance and create a timestamp file in it
+     * snapshot the instance
+     * boot a second instance from the snapshot
+     * check the existence of the timestamp file in the second instance
+
+    """
+
+    def _wait_for_server_status(self, server, status):
+        self.status_timeout(self.compute_client.servers,
+                            server.id,
+                            status)
+
+    def _wait_for_image_status(self, image_id, status):
+        self.status_timeout(self.image_client.images, image_id, status)
+
+    def _boot_image(self, image_id):
+        name = rand_name('scenario-server-')
+        client = self.compute_client
+        flavor_id = self.config.compute.flavor_ref
+        LOG.debug("name:%s, image:%s" % (name, image_id))
+        server = client.servers.create(name=name,
+                                       image=image_id,
+                                       flavor=flavor_id,
+                                       key_name=self.keypair.name)
+        self.addCleanup(self.compute_client.servers.delete, server)
+        self.assertEqual(name, server.name)
+        self._wait_for_server_status(server, 'ACTIVE')
+        server = client.servers.get(server)  # getting network information
+        LOG.debug("server:%s" % server)
+        return server
+
+    def _add_keypair(self):
+        name = rand_name('scenario-keypair-')
+        self.keypair = self.compute_client.keypairs.create(name=name)
+        self.addCleanup(self.compute_client.keypairs.delete, self.keypair)
+        self.assertEqual(name, self.keypair.name)
+
+    def _create_security_group_rule(self):
+        sgs = self.compute_client.security_groups.list()
+        for sg in sgs:
+            if sg.name == 'default':
+                secgroup = sg
+
+        ruleset = {
+            # ssh
+            'ip_protocol': 'tcp',
+            'from_port': 22,
+            'to_port': 22,
+            'cidr': '0.0.0.0/0',
+            'group_id': None
+        }
+        sg_rule = self.compute_client.security_group_rules.create(secgroup.id,
+                                                                  **ruleset)
+        self.addCleanup(self.compute_client.security_group_rules.delete,
+                        sg_rule.id)
+
+    def _ssh_to_server(self, server):
+        username = self.config.scenario.ssh_user
+        ip = server.networks[self.config.compute.network_for_ssh][0]
+        linux_client = RemoteClient(ip,
+                                    username,
+                                    pkey=self.keypair.private_key)
+
+        return linux_client.ssh_client
+
+    def _write_timestamp(self, server):
+        ssh_client = self._ssh_to_server(server)
+        ssh_client.exec_command('date > /tmp/timestamp; sync')
+        self.timestamp = ssh_client.exec_command('cat /tmp/timestamp')
+
+    def _create_image(self, server):
+        snapshot_name = rand_name('scenario-snapshot-')
+        create_image_client = self.compute_client.servers.create_image
+        image_id = create_image_client(server, snapshot_name)
+        self.addCleanup(self.image_client.images.delete, image_id)
+        self._wait_for_server_status(server, 'ACTIVE')
+        self._wait_for_image_status(image_id, 'active')
+        snapshot_image = self.image_client.images.get(image_id)
+        self.assertEquals(snapshot_name, snapshot_image.name)
+        return image_id
+
+    def _check_timestamp(self, server):
+        ssh_client = self._ssh_to_server(server)
+        got_timestamp = ssh_client.exec_command('cat /tmp/timestamp')
+        self.assertEqual(self.timestamp, got_timestamp)
+
+    def test_snapshot_pattern(self):
+        # prepare for booting a instance
+        self._add_keypair()
+        self._create_security_group_rule()
+
+        # boot a instance and create a timestamp file in it
+        server = self._boot_image(self.config.compute.image_ref)
+        self._write_timestamp(server)
+
+        # snapshot the instance
+        snapshot_image_id = self._create_image(server)
+
+        # boot a second instance from the snapshot
+        server_from_snapshot = self._boot_image(snapshot_image_id)
+
+        # check the existence of the timestamp file in the second instance
+        self._check_timestamp(server_from_snapshot)
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index dc3c524..8093d19 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -1,4 +1,21 @@
+# 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.
+
 import json
+import urllib
 
 from tempest.common.rest_client import RestClient
 
@@ -10,10 +27,13 @@
                                               auth_url, tenant_name)
         self.service = self.config.compute.catalog_type
 
-    def list_hosts(self):
+    def list_hosts(self, params=None):
         """Lists all hosts."""
 
         url = 'os-hosts'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
         resp, body = self.get(url)
         body = json.loads(body)
         return resp, body['hosts']
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index d4822da..6906610 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -332,15 +332,6 @@
                                req_body, self.headers)
         return resp, body
 
-    def list_servers_for_all_tenants(self):
-
-        url = self.base_url + '/servers?all_tenants=1'
-        resp = self.requests.get(url)
-        resp, body = self.get('servers', self.headers)
-
-        body = json.loads(body)
-        return resp, body['servers']
-
     def migrate_server(self, server_id, **kwargs):
         """Migrates a server to a new host."""
         return self.action(server_id, 'migrate', None, **kwargs)
diff --git a/tempest/services/compute/xml/hosts_client.py b/tempest/services/compute/xml/hosts_client.py
new file mode 100644
index 0000000..70aeb48
--- /dev/null
+++ b/tempest/services/compute/xml/hosts_client.py
@@ -0,0 +1,41 @@
+# 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.
+
+import urllib
+
+from lxml import etree
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class HostsClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(HostsClientXML, self).__init__(config, username, password,
+                                             auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def list_hosts(self, params=None):
+        """Lists all hosts."""
+
+        url = 'os-hosts'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        node = etree.fromstring(body)
+        body = [xml_to_json(x) for x in node.getchildren()]
+        return resp, body
diff --git a/tempest/services/compute/xml/security_groups_client.py b/tempest/services/compute/xml/security_groups_client.py
index 4f9d347..5d86790 100644
--- a/tempest/services/compute/xml/security_groups_client.py
+++ b/tempest/services/compute/xml/security_groups_client.py
@@ -98,7 +98,9 @@
         """
         group_rule = Element("security_group_rule")
 
-        elements = {k: kwargs.get(k) for k in ('cidr', 'group_id')}
+        elements = dict()
+        elements['cidr'] = kwargs.get('cidr')
+        elements['group_id'] = kwargs.get('group_id')
         elements['parent_group_id'] = parent_group_id
         elements['ip_protocol'] = ip_proto
         elements['from_port'] = from_port
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 1ec4df0..f2cca72 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -163,6 +163,22 @@
         server = self._parse_server(etree.fromstring(body))
         return resp, server
 
+    def suspend_server(self, server_id, **kwargs):
+        """Suspends the provided server."""
+        return self.action(server_id, 'suspend', None, **kwargs)
+
+    def resume_server(self, server_id, **kwargs):
+        """Un-suspends the provided server."""
+        return self.action(server_id, 'resume', None, **kwargs)
+
+    def pause_server(self, server_id, **kwargs):
+        """Pauses the provided server."""
+        return self.action(server_id, 'pause', None, **kwargs)
+
+    def unpause_server(self, server_id, **kwargs):
+        """Un-pauses the provided server."""
+        return self.action(server_id, 'unpause', None, **kwargs)
+
     def delete_server(self, server_id):
         """Deletes the given server."""
         return self.delete("servers/%s" % str(server_id))
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index f0b1c28..dac77a2 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -88,7 +88,7 @@
                 obj_size = obj.tell()
                 obj.seek(0)
                 return obj_size
-            except IOError, e:
+            except IOError as e:
                 if e.errno == errno.ESPIPE:
                     # Illegal seek. This means the user is trying
                     # to pipe image data to the client, e.g.
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 4758ddd..c4fe6b1 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -5,10 +5,10 @@
 class NetworkClient(RestClient):
 
     """
-    Tempest REST client for Quantum. Uses v2 of the Quantum API, since the
+    Tempest REST client for Neutron. Uses v2 of the Neutron API, since the
     V1 API has been removed from the code base.
 
-    Implements the following operations for each one of the basic Quantum
+    Implements the following operations for each one of the basic Neutron
     abstractions (networks, sub-networks and ports):
 
     create
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index 81162df..22f3f26 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -16,6 +16,7 @@
 #    under the License.
 
 import json
+import re
 import time
 import urllib
 
@@ -45,6 +46,35 @@
 
     def create_stack(self, name, disable_rollback=True, parameters={},
                      timeout_mins=60, template=None, template_url=None):
+        headers, body = self._prepare_update_create(
+            name,
+            disable_rollback,
+            parameters,
+            timeout_mins,
+            template,
+            template_url)
+        uri = 'stacks'
+        resp, body = self.post(uri, headers=headers, body=body)
+        return resp, body
+
+    def update_stack(self, stack_identifier, name, disable_rollback=True,
+                     parameters={}, timeout_mins=60, template=None,
+                     template_url=None):
+        headers, body = self._prepare_update_create(
+            name,
+            disable_rollback,
+            parameters,
+            timeout_mins,
+            template,
+            template_url)
+
+        uri = "stacks/%s" % stack_identifier
+        resp, body = self.put(uri, headers=headers, body=body)
+        return resp, body
+
+    def _prepare_update_create(self, name, disable_rollback=True,
+                               parameters={}, timeout_mins=60,
+                               template=None, template_url=None):
         post_body = {
             "stack_name": name,
             "disable_rollback": disable_rollback,
@@ -57,9 +87,13 @@
         if template_url:
             post_body['template_url'] = template_url
         body = json.dumps(post_body)
-        uri = 'stacks'
-        resp, body = self.post(uri, headers=self.headers, body=body)
-        return resp, body
+
+        # Password must be provided on stack create so that heat
+        # can perform future operations on behalf of the user
+        headers = dict(self.headers)
+        headers['X-Auth-Key'] = self.password
+        headers['X-Auth-User'] = self.user
+        return headers, body
 
     def get_stack(self, stack_identifier):
         """Returns the details of a single stack."""
@@ -68,24 +102,69 @@
         body = json.loads(body)
         return resp, body['stack']
 
+    def list_resources(self, stack_identifier):
+        """Returns the details of a single resource."""
+        url = "stacks/%s/resources" % stack_identifier
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['resources']
+
+    def get_resource(self, stack_identifier, resource_name):
+        """Returns the details of a single resource."""
+        url = "stacks/%s/resources/%s" % (stack_identifier, resource_name)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['resource']
+
     def delete_stack(self, stack_identifier):
         """Deletes the specified Stack."""
         return self.delete("stacks/%s" % str(stack_identifier))
 
-    def wait_for_stack_status(self, stack_identifier, status, failure_status=(
-            'CREATE_FAILED',
-            'DELETE_FAILED',
-            'UPDATE_FAILED',
-            'ROLLBACK_FAILED')):
-        """Waits for a Volume to reach a given status."""
-        stack_status = None
+    def wait_for_resource_status(self, stack_identifier, resource_name,
+                                 status, failure_pattern='^.*_FAILED$'):
+        """Waits for a Resource to reach a given status."""
         start = int(time.time())
+        fail_regexp = re.compile(failure_pattern)
 
-        while stack_status != status:
+        while True:
+            try:
+                resp, body = self.get_resource(
+                    stack_identifier, resource_name)
+            except exceptions.NotFound:
+                # ignore this, as the resource may not have
+                # been created yet
+                pass
+            else:
+                resource_name = body['logical_resource_id']
+                resource_status = body['resource_status']
+                if resource_status == status:
+                    return
+                if fail_regexp.search(resource_status):
+                    raise exceptions.StackBuildErrorException(
+                        stack_identifier=stack_identifier,
+                        resource_status=resource_status,
+                        resource_status_reason=body['resource_status_reason'])
+
+            if int(time.time()) - start >= self.build_timeout:
+                message = ('Resource %s failed to reach %s status within '
+                           'the required time (%s s).' %
+                           (resource_name, status, self.build_timeout))
+                raise exceptions.TimeoutException(message)
+            time.sleep(self.build_interval)
+
+    def wait_for_stack_status(self, stack_identifier, status,
+                              failure_pattern='^.*_FAILED$'):
+        """Waits for a Stack to reach a given status."""
+        start = int(time.time())
+        fail_regexp = re.compile(failure_pattern)
+
+        while True:
             resp, body = self.get_stack(stack_identifier)
             stack_name = body['stack_name']
             stack_status = body['stack_status']
-            if stack_status in failure_status:
+            if stack_status == status:
+                return
+            if fail_regexp.search(stack_status):
                 raise exceptions.StackBuildErrorException(
                     stack_identifier=stack_identifier,
                     stack_status=stack_status,
diff --git a/tempest/stress/actions/volume_create_delete.py b/tempest/stress/actions/volume_create_delete.py
new file mode 100644
index 0000000..e0c95b5
--- /dev/null
+++ b/tempest/stress/actions/volume_create_delete.py
@@ -0,0 +1,30 @@
+#    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.utils.data_utils import rand_name
+
+
+def create_delete(manager, logger):
+    while True:
+        name = rand_name("volume")
+        logger.info("creating %s" % name)
+        resp, volume = manager.volumes_client.create_volume(size=1,
+                                                            display_name=name)
+        assert(resp.status == 200)
+        manager.volumes_client.wait_for_volume_status(volume['id'],
+                                                      'available')
+        logger.info("created %s" % volume['id'])
+        logger.info("deleting %s" % name)
+        resp, _ = manager.volumes_client.delete_volume(volume['id'])
+        assert(resp.status == 202)
+        manager.volumes_client.wait_for_resource_deletion(volume['id'])
+        logger.info("deleted %s" % volume['id'])
diff --git a/tempest/stress/cleanup.py b/tempest/stress/cleanup.py
index b2cb70a..3b1c871 100644
--- a/tempest/stress/cleanup.py
+++ b/tempest/stress/cleanup.py
@@ -58,3 +58,16 @@
     for tenant in tenants:
         if tenant['name'].startswith("stress_tenant"):
             admin_manager.identity_client.delete_tenant(tenant['id'])
+
+    _, vols = admin_manager.volumes_client.list_volumes({"all_tenants": True})
+    for v in vols:
+        try:
+            admin_manager.volumes_client.delete_volume(v['id'])
+        except Exception:
+            pass
+
+    for v in vols:
+        try:
+            admin_manager.volumes_client.wait_for_resource_deletion(v['id'])
+        except Exception:
+            pass
diff --git a/tempest/stress/etc/volume-create-delete-test.json b/tempest/stress/etc/volume-create-delete-test.json
new file mode 100644
index 0000000..ed0aaeb
--- /dev/null
+++ b/tempest/stress/etc/volume-create-delete-test.json
@@ -0,0 +1,7 @@
+[{"action": "tempest.stress.actions.volume_create_delete.create_delete",
+  "threads": 4,
+  "use_admin": false,
+  "use_isolated_tenants": false,
+  "kwargs": {}
+  }
+]
diff --git a/tempest/test.py b/tempest/test.py
index 5f403b3..6be37be 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -27,6 +27,9 @@
 
 LOG = logging.getLogger(__name__)
 
+# All the successful HTTP status codes from RFC 2616
+HTTP_SUCCESS = (200, 201, 202, 203, 204, 205, 206)
+
 
 def attr(*args, **kwargs):
     """A decorator which applies the nose and testtools attr decorator
diff --git a/tempest/whitebox/manager.py b/tempest/whitebox/manager.py
index aa58ab6..3bd057c 100644
--- a/tempest/whitebox/manager.py
+++ b/tempest/whitebox/manager.py
@@ -128,7 +128,7 @@
             meta = MetaData()
             meta.reflect(bind=engine)
 
-        except Exception, e:
+        except Exception as e:
             raise exceptions.SQLException(message=e)
 
         return connection, meta
diff --git a/test-requirements.txt b/test-requirements.txt
index 3912695..2185997 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,7 +3,6 @@
 pyflakes==0.7.2
 flake8==2.0
 hacking>=0.5.3,<0.6
-psycopg2
 # needed for doc build
 sphinx>=1.1.2
 
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
index 3129484..0ce1500 100755
--- a/tools/find_stack_traces.py
+++ b/tools/find_stack_traces.py
@@ -110,7 +110,7 @@
 
 
 def usage():
-    print """
+    print("""
 Usage: find_stack_traces.py <logurl>
 
 Hunts for stack traces in a devstack run. Must provide it a base log url
@@ -118,20 +118,20 @@
 
 Returns a report listing stack traces out of the various files where
 they are found.
-"""
+""")
     sys.exit(0)
 
 
 def print_stats(items, fname, verbose=False):
     errors = len(filter(lambda x: x.level == "ERROR", items))
     traces = len(filter(lambda x: x.level == "TRACE", items))
-    print "%d ERRORS found in %s" % (errors, fname)
-    print "%d TRACES found in %s" % (traces, fname)
+    print("%d ERRORS found in %s" % (errors, fname))
+    print("%d TRACES found in %s" % (traces, fname))
 
     if verbose:
         for item in items:
-            print item
-        print "\n\n"
+            print(item)
+        print("\n\n")
 
 
 def main():
diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py
index fd9076f..f428c1e 100644
--- a/tools/install_venv_common.py
+++ b/tools/install_venv_common.py
@@ -1,6 +1,6 @@
 # vim: tabstop=4 shiftwidth=4 softtabstop=4
 
-# Copyright 2013 OpenStack, LLC
+# Copyright 2013 OpenStack Foundation
 # Copyright 2013 IBM Corp.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -18,10 +18,15 @@
 """Provides methods needed by installation script for OpenStack development
 virtual environments.
 
+Since this script is used to bootstrap a virtualenv from the system's Python
+environment, it should be kept strictly compatible with Python 2.6.
+
 Synced in from openstack-common
 """
 
-import argparse
+from __future__ import print_function
+
+import optparse
 import os
 import subprocess
 import sys
@@ -29,17 +34,18 @@
 
 class InstallVenv(object):
 
-    def __init__(self, root, venv, pip_requires, test_requires, py_version,
+    def __init__(self, root, venv, requirements,
+                 test_requirements, py_version,
                  project):
         self.root = root
         self.venv = venv
-        self.pip_requires = pip_requires
-        self.test_requires = test_requires
+        self.requirements = requirements
+        self.test_requirements = test_requirements
         self.py_version = py_version
         self.project = project
 
     def die(self, message, *args):
-        print >> sys.stderr, message % args
+        print(message % args, file=sys.stderr)
         sys.exit(1)
 
     def check_python_version(self):
@@ -70,11 +76,13 @@
     def get_distro(self):
         if (os.path.exists('/etc/fedora-release') or
                 os.path.exists('/etc/redhat-release')):
-            return Fedora(self.root, self.venv, self.pip_requires,
-                          self.test_requires, self.py_version, self.project)
+            return Fedora(
+                self.root, self.venv, self.requirements,
+                self.test_requirements, self.py_version, self.project)
         else:
-            return Distro(self.root, self.venv, self.pip_requires,
-                          self.test_requires, self.py_version, self.project)
+            return Distro(
+                self.root, self.venv, self.requirements,
+                self.test_requirements, self.py_version, self.project)
 
     def check_dependencies(self):
         self.get_distro().install_virtualenv()
@@ -86,20 +94,15 @@
         virtual environment.
         """
         if not os.path.isdir(self.venv):
-            print 'Creating venv...',
+            print('Creating venv...', end=' ')
             if no_site_packages:
                 self.run_command(['virtualenv', '-q', '--no-site-packages',
                                  self.venv])
             else:
                 self.run_command(['virtualenv', '-q', self.venv])
-            print 'done.'
-            print 'Installing pip in venv...',
-            if not self.run_command(['tools/with_venv.sh', 'easy_install',
-                                    'pip>1.0']).strip():
-                self.die("Failed to install pip.")
-            print 'done.'
+            print('done.')
         else:
-            print "venv already exists..."
+            print("venv already exists...")
             pass
 
     def pip_install(self, *args):
@@ -108,35 +111,27 @@
                          redirect_output=False)
 
     def install_dependencies(self):
-        print 'Installing dependencies with pip (this can take a while)...'
+        print('Installing dependencies with pip (this can take a while)...')
 
         # First things first, make sure our venv has the latest pip and
-        # distribute.
-        # NOTE: we keep pip at version 1.1 since the most recent version causes
-        # the .venv creation to fail. See:
-        # https://bugs.launchpad.net/nova/+bug/1047120
-        self.pip_install('pip==1.1')
-        self.pip_install('distribute')
+        # setuptools.
+        self.pip_install('pip>=1.3')
+        self.pip_install('setuptools')
 
-        # Install greenlet by hand - just listing it in the requires file does
-        # not
-        # get it installed in the right order
-        self.pip_install('greenlet')
-
-        self.pip_install('-r', self.pip_requires)
-        self.pip_install('-r', self.test_requires)
+        self.pip_install('-r', self.requirements)
+        self.pip_install('-r', self.test_requirements)
 
     def post_process(self):
         self.get_distro().post_process()
 
     def parse_args(self, argv):
         """Parses command-line arguments."""
-        parser = argparse.ArgumentParser()
-        parser.add_argument('-n', '--no-site-packages',
-                            action='store_true',
-                            help="Do not inherit packages from global Python "
-                                 "install")
-        return parser.parse_args(argv[1:])
+        parser = optparse.OptionParser()
+        parser.add_option('-n', '--no-site-packages',
+                          action='store_true',
+                          help="Do not inherit packages from global Python "
+                               "install")
+        return parser.parse_args(argv[1:])[0]
 
 
 class Distro(InstallVenv):
@@ -150,12 +145,12 @@
             return
 
         if self.check_cmd('easy_install'):
-            print 'Installing virtualenv via easy_install...',
+            print('Installing virtualenv via easy_install...', end=' ')
             if self.run_command(['easy_install', 'virtualenv']):
-                print 'Succeeded'
+                print('Succeeded')
                 return
             else:
-                print 'Failed'
+                print('Failed')
 
         self.die('ERROR: virtualenv not found.\n\n%s development'
                  ' requires virtualenv, please install it using your'
@@ -180,19 +175,16 @@
         return self.run_command_with_code(['rpm', '-q', pkg],
                                           check_exit_code=False)[1] == 0
 
-    def yum_install(self, pkg, **kwargs):
-        print "Attempting to install '%s' via yum" % pkg
-        self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs)
-
     def apply_patch(self, originalfile, patchfile):
-        self.run_command(['patch', originalfile, patchfile])
+        self.run_command(['patch', '-N', originalfile, patchfile],
+                         check_exit_code=False)
 
     def install_virtualenv(self):
         if self.check_cmd('virtualenv'):
             return
 
         if not self.check_pkg('python-virtualenv'):
-            self.yum_install('python-virtualenv', check_exit_code=False)
+            self.die("Please install 'python-virtualenv'.")
 
         super(Fedora, self).install_virtualenv()
 
@@ -205,12 +197,13 @@
         This can be removed when the fix is applied upstream.
 
         Nova: https://bugs.launchpad.net/nova/+bug/884915
-        Upstream: https://bitbucket.org/which_linden/eventlet/issue/89
+        Upstream: https://bitbucket.org/eventlet/eventlet/issue/89
+        RHEL: https://bugzilla.redhat.com/958868
         """
 
         # Install "patch" program if it's not there
         if not self.check_pkg('patch'):
-            self.yum_install('patch')
+            self.die("Please install 'patch'.")
 
         # Apply the eventlet patch
         self.apply_patch(os.path.join(self.venv, 'lib', self.py_version,
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index c7b0033..1ed6961 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -118,8 +118,8 @@
 
     unskips = sorted(set(unskips))
     if unskips:
-        print "The following bugs have been fixed and the corresponding skips"
-        print "should be removed from the test cases:"
-        print
+        print("The following bugs have been fixed and the corresponding skips")
+        print("should be removed from the test cases:")
+        print()
         for bug in unskips:
-            print "  %7s" % bug
+            print("  %7s" % bug)
diff --git a/tools/tempest_coverage.py b/tools/tempest_coverage.py
index 5b926f9..ef2eacd 100755
--- a/tools/tempest_coverage.py
+++ b/tools/tempest_coverage.py
@@ -12,7 +12,7 @@
 #    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
+#    under the License.
 
 import json
 import os
@@ -151,14 +151,14 @@
     elif CLI.command == 'stop':
         resp, body = coverage_client.stop_coverage()
         if not resp['status'] == '200':
-            print 'coverage stop failed with: %s:' % (resp['status'] + ': '
-                                                      + body)
+            print('coverage stop failed with: %s:' % (resp['status'] + ': '
+                                                      + body))
             exit(int(resp['status']))
         path = body['path']
         if CLI.output:
             shutil.copytree(path, CLI.output)
         else:
-            print "Data files located at: %s" % path
+            print("Data files located at: %s" % path)
 
     elif CLI.command == 'report':
         if CLI.xml:
@@ -169,8 +169,8 @@
         else:
             resp, body = coverage_client.report_coverage(file=CLI.filename)
         if not resp['status'] == '200':
-            print 'coverage report failed with: %s:' % (resp['status'] + ': '
-                                                        + body)
+            print('coverage report failed with: %s:' % (resp['status'] + ': '
+                                                        + body))
             exit(int(resp['status']))
         path = body['path']
         if CLI.output:
@@ -182,10 +182,10 @@
         else:
             if not CLI.html:
                 path = os.path.dirname(path)
-            print 'Report files located at: %s' % path
+            print('Report files located at: %s' % path)
 
     else:
-        print 'Invalid command'
+        print('Invalid command')
         exit(1)
 
 
diff --git a/tox.ini b/tox.ini
index caa9403..964dbca 100644
--- a/tox.ini
+++ b/tox.ini
@@ -61,6 +61,14 @@
    nosetests --logging-format '%(asctime)-15s %(message)s' --with-xunit --xunit-file=nosetests-full.xml -sv tempest/api tempest/scenario tempest/thirdparty tempest/cli
    python -m tools/tempest_coverage -c report --html {posargs}
 
+[testenv:stress]
+sitepackages = True
+setenv = VIRTUAL_ENV={envdir}
+commands =
+    python -m tempest/stress/run_stress tempest/stress/etc/sample-test.json -d 60
+    python -m tempest/stress/run_stress tempest/stress/etc/volume-create-delete-test.json -d 60
+
+
 [testenv:venv]
 commands = {posargs}
 deps = -r{toxinidir}/requirements.txt