Merge "add image tests v2"
diff --git a/.testr.conf b/.testr.conf
index 9a72d29..510f4c9 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -1,5 +1,8 @@
 [DEFAULT]
-test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./tempest $LISTOPT $IDOPTION
+test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
+             OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
+             OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-250} \
+             ${PYTHON:-python} -m subunit.run discover -t ./ ./tempest $LISTOPT $IDOPTION
 test_id_option=--load-list $IDFILE
 test_list_option=--list
 group_regex=([^\.]*\.)*
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 033bc82..3aa0497 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -1,3 +1,15 @@
+[DEFAULT]
+# log_config = /opt/stack/tempest/etc/logging.conf.sample
+
+# disable logging to the stderr
+use_stderr = False
+
+# log file
+log_file = tempest.log
+
+# lock/semaphore base directory
+lock_path=/tmp
+
 [identity]
 # This section contains configuration options that a variety of Tempest
 # test clients use when authenticating with different user/tenant
diff --git a/openstack-common.conf b/openstack-common.conf
index 8568f22..ff84404 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -4,6 +4,7 @@
 module=install_venv_common
 module=lockutils
 module=log
+module=importlib
 
 # The base module to hold the copy of openstack.common
 base=tempest
diff --git a/requirements.txt b/requirements.txt
index 06aa9f3..cc61b01 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -19,3 +19,4 @@
 oslo.config>=1.1.0
 # Needed for whitebox testing
 sqlalchemy
+eventlet>=0.12.0
diff --git a/tempest/api/compute/__init__.py b/tempest/api/compute/__init__.py
index fb96b4a..fd26081 100644
--- a/tempest/api/compute/__init__.py
+++ b/tempest/api/compute/__init__.py
@@ -15,9 +15,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common import log as logging
 from tempest import config
 from tempest.exceptions import InvalidConfiguration
+from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index b66bd7e..107d635 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -18,6 +18,7 @@
 from tempest.api.compute import base
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
+from tempest.openstack.common import lockutils
 from tempest.test import attr
 
 
@@ -197,6 +198,7 @@
         self.assertIn(self.host, body['hosts'])
 
     @attr(type='gate')
+    @lockutils.synchronized('availability_zone', 'tempest-', True)
     def test_aggregate_add_host_create_server_with_az(self):
         # Add an host to the given aggregate and create a server.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -205,7 +207,6 @@
         self.addCleanup(self.client.delete_aggregate, aggregate['id'])
         self.client.add_host(aggregate['id'], self.host)
         self.addCleanup(self.client.remove_host, aggregate['id'], self.host)
-
         server_name = rand_name('test_server_')
         servers_client = self.servers_client
         admin_servers_client = self.os_adm.servers_client
diff --git a/tempest/api/compute/admin/test_services.py b/tempest/api/compute/admin/test_services.py
index ce16353..dc960db 100644
--- a/tempest/api/compute/admin/test_services.py
+++ b/tempest/api/compute/admin/test_services.py
@@ -69,7 +69,10 @@
         # lookups, so only compare binary lists.
         s1 = map(lambda x: x['binary'], services)
         s2 = map(lambda x: x['binary'], services_on_host)
-        self.assertEqual(s1, s2)
+
+        #sort the lists before comparing, to take out dependency
+        #on order.
+        self.assertEqual(sorted(s1), sorted(s2))
 
     @attr(type=['negative', 'gate'])
     def test_get_service_by_invalid_params(self):
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 8ba074e..d40b0e0 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -19,9 +19,9 @@
 
 from tempest.api import compute
 from tempest import clients
-from tempest.common import log as logging
 from tempest.common.utils.data_utils import parse_image_id
 from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
 import tempest.test
 
 
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index b27d710..fb2906a 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -16,9 +16,9 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common import log as logging
 from tempest.common.utils.data_utils import parse_image_id
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 from tempest.test import attr
 
 
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 13c2f74..b35b1b2 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -110,6 +110,11 @@
         self.assertEqual(202, resp.status)
         self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
 
+    def _unpause(self, server_id):
+        resp, body = self.servers_client.unpause_server(server_id)
+        self.assertEqual(202, resp.status)
+        self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+
     @attr(type='smoke')
     def test_rescue_unrescue_instance(self):
         resp, body = self.servers_client.rescue_server(
@@ -121,6 +126,18 @@
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
 
     @attr(type=['negative', 'gate'])
+    def test_rescue_paused_instance(self):
+        #Rescue a paused server
+        resp, body = self.servers_client.pause_server(
+            self.server_id)
+        self.addCleanup(self._unpause, self.server_id)
+        self.assertEqual(202, resp.status)
+        self.servers_client.wait_for_server_status(self.server_id, 'PAUSED')
+        self.assertRaises(exceptions.Duplicate,
+                          self.servers_client.rescue_server,
+                          self.server_id)
+
+    @attr(type=['negative', 'gate'])
     def test_rescued_vm_reboot(self):
         self.assertRaises(exceptions.Duplicate, self.servers_client.reboot,
                           self.rescue_id, 'HARD')
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index af58b5f..703f143 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -221,13 +221,10 @@
     @attr(type=['negative', 'gate'])
     def test_delete_a_server_of_another_tenant(self):
         # Delete a server that belongs to another tenant
-        try:
-            resp, server = self.create_server(wait_until='ACTIVE')
-            self.assertRaises(exceptions.NotFound,
-                              self.alt_client.delete_server,
-                              server['id'])
-        finally:
-            self.client.delete_server(server['id'])
+        resp, server = self.create_server(wait_until='ACTIVE')
+        self.assertRaises(exceptions.NotFound,
+                          self.alt_client.delete_server,
+                          server['id'])
 
     @attr(type=['negative', 'gate'])
     def test_delete_server_pass_negative_id(self):
diff --git a/tempest/api/identity/__init__.py b/tempest/api/identity/__init__.py
index 718aa15..0ed47f5 100644
--- a/tempest/api/identity/__init__.py
+++ b/tempest/api/identity/__init__.py
@@ -15,7 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 062d63e..086c50e 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -15,9 +15,9 @@
 #    under the License.
 
 from tempest import clients
-from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 import tempest.test
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 142ad7d..8785e31 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -38,6 +38,11 @@
 
         tenant_network_mask_bits with the mask bits to be used to partition the
         block defined by tenant-network_cidr
+
+    Finally, it is assumed that the following option is defined in the
+    [service_available] section of etc/tempest.conf
+
+        neutron as True
     """
 
     @classmethod
diff --git a/tempest/api/network/test_quotas.py b/tempest/api/network/test_quotas.py
new file mode 100644
index 0000000..ba70f34
--- /dev/null
+++ b/tempest/api/network/test_quotas.py
@@ -0,0 +1,91 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack, LLC
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+from tempest.api.network import base
+from tempest import clients
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+class QuotasTest(base.BaseNetworkTest):
+
+    """
+    Tests the following operations in the Neutron API using the REST client for
+    Neutron:
+
+        list quotas for tenants who have non-default quota values
+        show quotas for a specified tenant
+        update quotas for a specified tenant
+        reset quotas to default values for a specified tenant
+
+    v2.0 of the API is assumed. It is also assumed that the following
+    option is defined in the [service_available] section of etc/tempest.conf:
+
+        neutron as True
+
+    Finally, it is assumed that the per-tenant quota extension API is
+    configured in /etc/neutron/neutron.conf as follows:
+
+        quota_driver = neutron.db.quota_db.DbQuotaDriver
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        super(QuotasTest, cls).setUpClass()
+        admin_manager = clients.AdminManager()
+        cls.admin_client = admin_manager.network_client
+        cls.identity_admin_client = admin_manager.identity_client
+
+    @attr(type='gate')
+    def test_quotas(self):
+        # Add a tenant to conduct the test
+        test_tenant = rand_name('test_tenant_')
+        test_description = rand_name('desc_')
+        _, tenant = self.identity_admin_client.create_tenant(
+            name=test_tenant,
+            description=test_description)
+        tenant_id = tenant['id']
+        self.addCleanup(self.identity_admin_client.delete_tenant, tenant_id)
+        # Change quotas for tenant
+        new_quotas = {'network': 0, 'security_group': 0}
+        resp, quota_set = self.admin_client.update_quotas(tenant_id,
+                                                          **new_quotas)
+        self.assertEqual('200', resp['status'])
+        self.addCleanup(self.admin_client.reset_quotas, tenant_id)
+        self.assertEqual(0, quota_set['network'])
+        self.assertEqual(0, quota_set['security_group'])
+        # Confirm our tenant is listed among tenants with non default quotas
+        resp, non_default_quotas = self.admin_client.list_quotas()
+        self.assertEqual('200', resp['status'])
+        found = False
+        for qs in non_default_quotas:
+            if qs['tenant_id'] == tenant_id:
+                found = True
+        self.assertTrue(found)
+        # Confirm from APi quotas were changed as requested for tenant
+        resp, quota_set = self.admin_client.show_quotas(tenant_id)
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(0, quota_set['network'])
+        self.assertEqual(0, quota_set['security_group'])
+        # Reset quotas to default and confirm
+        resp, body = self.admin_client.reset_quotas(tenant_id)
+        self.assertEqual('204', resp['status'])
+        resp, non_default_quotas = self.admin_client.list_quotas()
+        self.assertEqual('200', resp['status'])
+        for q in non_default_quotas:
+            self.assertNotEqual(tenant_id, q['tenant_id'])
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index a0b248c..d06d942 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -12,11 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common import log as logging
 import time
 
 from tempest import clients
 from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
 import tempest.test
 
 
diff --git a/tempest/api/orchestration/stacks/test_instance_cfn_init.py b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
index 4f22158..7897b70 100644
--- a/tempest/api/orchestration/stacks/test_instance_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
@@ -13,13 +13,13 @@
 #    under the License.
 
 import json
-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.openstack.common import log as logging
 from tempest.test import attr
 
 
diff --git a/tempest/api/orchestration/stacks/test_stacks.py b/tempest/api/orchestration/stacks/test_stacks.py
index 15979ed..f1f1f7e 100644
--- a/tempest/api/orchestration/stacks/test_stacks.py
+++ b/tempest/api/orchestration/stacks/test_stacks.py
@@ -12,10 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common import log as logging
-
 from tempest.api.orchestration import base
 from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
 from tempest.test import attr
 
 
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 086b981..13d0d48 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -13,8 +13,8 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
 from tempest.services.volume.json.admin import volume_types_client
 from tempest.services.volume.json import volumes_client
 from tempest.test import attr
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index a84f9e8..bb0047d 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -18,7 +18,7 @@
 import time
 
 from tempest import clients
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
 import tempest.test
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 91b44da..5861497 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -106,3 +106,4 @@
         self.addCleanup(self.image_client.delete_image, image_id)
         self.assertEqual(202, resp.status)
         self.image_client.wait_for_image_status(image_id, 'active')
+        self.client.wait_for_volume_status(self.volume['id'], 'available')
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 602209a..0328b44 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -13,8 +13,8 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
 from tempest.test import attr
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index f696180..00e025d 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -22,7 +22,7 @@
 from oslo.config import cfg
 
 import tempest.cli.output_parser
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
 import tempest.test
 
 
diff --git a/tempest/cli/output_parser.py b/tempest/cli/output_parser.py
index 3ee3098..bfd7f9e 100644
--- a/tempest/cli/output_parser.py
+++ b/tempest/cli/output_parser.py
@@ -17,11 +17,10 @@
 
 """Collection of utilities for parsing CLI clients output."""
 
-
-from tempest.common import log as logging
-
 import re
 
+from tempest.openstack.common import log as logging
+
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/cli/simple_read_only/test_compute.py b/tempest/cli/simple_read_only/test_compute.py
index 5dadbeb..e60e238 100644
--- a/tempest/cli/simple_read_only/test_compute.py
+++ b/tempest/cli/simple_read_only/test_compute.py
@@ -21,7 +21,7 @@
 import testtools
 
 import tempest.cli
-from tempest.common import log as logging
+from tempest.openstack.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 802a206..1848827 100644
--- a/tempest/cli/simple_read_only/test_compute_manage.py
+++ b/tempest/cli/simple_read_only/test_compute_manage.py
@@ -18,7 +18,7 @@
 import subprocess
 
 import tempest.cli
-from tempest.common import log as logging
+from tempest.openstack.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 fa77e8a..3d58451 100644
--- a/tempest/cli/simple_read_only/test_glance.py
+++ b/tempest/cli/simple_read_only/test_glance.py
@@ -19,7 +19,7 @@
 import subprocess
 
 import tempest.cli
-from tempest.common import log as logging
+from tempest.openstack.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 3bc8b3e..4002081 100644
--- a/tempest/cli/simple_read_only/test_keystone.py
+++ b/tempest/cli/simple_read_only/test_keystone.py
@@ -19,7 +19,7 @@
 import subprocess
 
 import tempest.cli
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
 
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/simple_read_only/test_neutron.py b/tempest/cli/simple_read_only/test_neutron.py
index 3b93696..7b8340d 100644
--- a/tempest/cli/simple_read_only/test_neutron.py
+++ b/tempest/cli/simple_read_only/test_neutron.py
@@ -19,10 +19,9 @@
 import subprocess
 
 from oslo.config import cfg
-import testtools
 
 import tempest.cli
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
 
 CONF = cfg.CONF
 
@@ -36,9 +35,13 @@
     These tests do not presume any content, nor do they create
     their own. They only verify the structure of output if present.
     """
-    if (not CONF.service_available.neutron):
-        msg = "Skiping all Neutron cli tests because it is not available"
-        raise testtools.TestCase.skipException(msg)
+
+    @classmethod
+    def setUpClass(cls):
+        if (not CONF.service_available.neutron):
+            msg = "Skiping all Neutron cli tests because it is not available"
+            raise cls.skipException(msg)
+        super(SimpleReadOnlyNeutronClientTest, cls).setUpClass()
 
     def test_neutron_fake_action(self):
         self.assertRaises(subprocess.CalledProcessError,
@@ -53,7 +56,8 @@
         self.assertTableStruct(ext, ['alias', 'name'])
 
     def test_neutron_dhcp_agent_list_hosting_net(self):
-        self.neutron('dhcp-agent-list-hosting-net', params="private")
+        self.neutron('dhcp-agent-list-hosting-net',
+                     params=CONF.compute.fixed_network_name)
 
     def test_neutron_agent_list(self):
         agents = self.parser.listing(self.neutron('agent-list'))
diff --git a/tempest/clients.py b/tempest/clients.py
index 02ab711..195cb89 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -15,9 +15,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common import log as logging
 from tempest import config
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 from tempest.services import botoclients
 from tempest.services.compute.json.aggregates_client import \
     AggregatesClientJSON
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index 4045430..831874d 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -34,9 +34,8 @@
 
 import OpenSSL
 
-from tempest.common import log as logging
 from tempest import exceptions as exc
-
+from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
 USER_AGENT = 'tempest'
diff --git a/tempest/common/log.py b/tempest/common/log.py
deleted file mode 100644
index 2159bfe..0000000
--- a/tempest/common/log.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# 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.
-
-import ConfigParser
-import inspect
-import logging
-import logging.config
-import os
-import re
-
-from oslo.config import cfg
-
-
-_DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
-_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
-
-_loggers = {}
-
-
-def getLogger(name='unknown'):
-    if len(_loggers) == 0:
-        loaded = _load_log_config()
-        getLogger.adapter = TestsAdapter if loaded else None
-
-    if name not in _loggers:
-        logger = logging.getLogger(name)
-        if getLogger.adapter:
-            _loggers[name] = getLogger.adapter(logger, name)
-        else:
-            _loggers[name] = logger
-
-    return _loggers[name]
-
-
-def _load_log_config():
-    conf_dir = os.environ.get('TEMPEST_LOG_CONFIG_DIR', None)
-    conf_file = os.environ.get('TEMPEST_LOG_CONFIG', None)
-    if not conf_dir or not conf_file:
-        return False
-
-    log_config = os.path.join(conf_dir, conf_file)
-    try:
-        logging.config.fileConfig(log_config)
-    except ConfigParser.Error as exc:
-        raise cfg.ConfigFileParseError(log_config, str(exc))
-    return True
-
-
-class TestsAdapter(logging.LoggerAdapter):
-
-    def __init__(self, logger, project_name):
-        self.logger = logger
-        self.project = project_name
-        self.regexp = re.compile(r"test_\w+\.py")
-
-    def __getattr__(self, key):
-        return getattr(self.logger, key)
-
-    def _get_test_name(self):
-        frames = inspect.stack()
-        for frame in frames:
-            binary_name = frame[1]
-            if self.regexp.search(binary_name) and 'self' in frame[0].f_locals:
-                return frame[0].f_locals.get('self').id()
-            elif frame[3] == '_run_cleanups':
-                #NOTE(myamazaki): method calling addCleanup
-                return frame[0].f_locals.get('self').case.id()
-            elif frame[3] in ['setUpClass', 'tearDownClass']:
-                #NOTE(myamazaki): setUpClass or tearDownClass
-                return "%s.%s.%s" % (frame[0].f_locals['cls'].__module__,
-                                     frame[0].f_locals['cls'].__name__,
-                                     frame[3])
-        return None
-
-    def process(self, msg, kwargs):
-        if 'extra' not in kwargs:
-            kwargs['extra'] = {}
-        extra = kwargs['extra']
-
-        test_name = self._get_test_name()
-        if test_name:
-            extra.update({'testname': test_name})
-        extra['extra'] = extra.copy()
-
-        return msg, kwargs
-
-
-class TestsFormatter(logging.Formatter):
-    def __init__(self, fmt=None, datefmt=None):
-        super(TestsFormatter, self).__init__()
-        self.default_format = _DEFAULT_LOG_FORMAT
-        self.testname_format =\
-            "%(asctime)s %(levelname)8s [%(testname)s] %(message)s"
-        self.datefmt = _DEFAULT_LOG_DATE_FORMAT
-
-    def format(self, record):
-        extra = record.__dict__.get('extra', None)
-        if extra and 'testname' in extra:
-            self._fmt = self.testname_format
-        else:
-            self._fmt = self.default_format
-        return logging.Formatter.format(self, record)
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index e94455d..09b87b2 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -24,8 +24,8 @@
 import re
 import time
 
-from tempest.common import log as logging
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 from tempest.services.compute.xml.common import xml_to_json
 
 # redrive rate limited calls at most twice
diff --git a/tempest/config.py b/tempest/config.py
index a918d0b..19170ae 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -23,10 +23,9 @@
 
 from oslo.config import cfg
 
-from tempest.common import log as logging
 from tempest.common.utils.misc import singleton
+from tempest.openstack.common import log as logging
 
-LOG = logging.getLogger(__name__)
 
 identity_group = cfg.OptGroup(name='identity',
                               title="Keystone Configuration Options")
@@ -603,7 +602,6 @@
     def __init__(self):
         """Initialize a configuration from a conf directory and conf file."""
         config_files = []
-
         failsafe_path = "/etc/tempest/" + self.DEFAULT_CONFIG_FILE
 
         # Environment variables override defaults...
@@ -618,8 +616,6 @@
                 'TEMPEST_CONFIG' in os.environ):
             path = failsafe_path
 
-        LOG.info("Using tempest config file %s" % path)
-
         if not os.path.exists(path):
             msg = "Config file %s not found" % path
             print(RuntimeError(msg), file=sys.stderr)
@@ -627,6 +623,9 @@
             config_files.append(path)
 
         cfg.CONF([], project='tempest', default_config_files=config_files)
+        logging.setup('tempest')
+        LOG = logging.getLogger('tempest')
+        LOG.info("Using tempest config file %s" % path)
 
         register_compute_opts(cfg.CONF)
         register_identity_opts(cfg.CONF)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 8b24b2e..e785299 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -29,10 +29,10 @@
 
 
 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
 import tempest.manager
+from tempest.openstack.common import log as logging
 import tempest.test
 
 
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
index 1f75e2f..39b1e10 100644
--- a/tempest/scenario/test_large_ops.py
+++ b/tempest/scenario/test_large_ops.py
@@ -15,8 +15,8 @@
 #    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.openstack.common import log as logging
 from tempest.scenario import manager
 
 
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 12227f6..13b31ec 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -15,10 +15,9 @@
 #    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.openstack.common import log as logging
 from tempest.scenario import manager
 
 
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 6202e91..8ee740e 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -15,8 +15,8 @@
 #    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.openstack.common import log as logging
 from tempest.scenario import manager
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index d318dd9..0ec3a1d 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -15,8 +15,8 @@
 #    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.openstack.common import log as logging
 from tempest.scenario import manager
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 76fac82..6e305c1 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -15,10 +15,9 @@
 #    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.openstack.common import log as logging
 from tempest.scenario import manager
 
 
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index c7721b6..4434604 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -20,10 +20,10 @@
 from cinderclient import exceptions as cinder_exceptions
 import testtools
 
-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 import exceptions
+from tempest.openstack.common import log as logging
 from tempest.scenario import manager
 import tempest.test
 
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index ea8b0e0..12e7034 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -21,9 +21,9 @@
 
 from lxml import etree
 
-from tempest.common import log as logging
 from tempest.common.rest_client import RestClientXML
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 from tempest.services.compute.xml.common import Document
 from tempest.services.compute.xml.common import Element
 from tempest.services.compute.xml.common import Text
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index dac77a2..bd48068 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -23,9 +23,9 @@
 import urllib
 
 from tempest.common import glance_http
-from tempest.common import log as logging
 from tempest.common.rest_client import RestClient
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 446a674..f96ed91 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -23,13 +23,11 @@
     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 Neutron
+    Implements create, delete, list and show for the basic Neutron
     abstractions (networks, sub-networks and ports):
 
-    create
-    delete
-    list
-    show
+    It also implements list, show, update and reset for OpenStack Networking
+    quotas
     """
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
@@ -128,3 +126,28 @@
         resp, body = self.get(uri, self.headers)
         body = json.loads(body)
         return resp, body
+
+    def update_quotas(self, tenant_id, **kwargs):
+        put_body = {'quota': kwargs}
+        body = json.dumps(put_body)
+        uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+        resp, body = self.put(uri, body, self.headers)
+        body = json.loads(body)
+        return resp, body['quota']
+
+    def show_quotas(self, tenant_id):
+        uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body['quota']
+
+    def reset_quotas(self, tenant_id):
+        uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+        resp, body = self.delete(uri, self.headers)
+        return resp, body
+
+    def list_quotas(self):
+        uri = '%s/quotas' % (self.uri_prefix)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body['quotas']
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index 17f6cba..034b452 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -16,9 +16,9 @@
 import time
 import urllib
 
-from tempest.common import log as logging
 from tempest.common.rest_client import RestClient
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index b35c43e..017ca95 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -17,9 +17,9 @@
 
 from lxml import etree
 
-from tempest.common import log as logging
 from tempest.common.rest_client import RestClientXML
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 from tempest.services.compute.xml.common import Document
 from tempest.services.compute.xml.common import Element
 from tempest.services.compute.xml.common import xml_to_json
diff --git a/tempest/stress/README.rst b/tempest/stress/README.rst
index 661763c..31642b0 100644
--- a/tempest/stress/README.rst
+++ b/tempest/stress/README.rst
@@ -30,7 +30,7 @@
 
 To test installation, do the following (from the tempest/stress directory):
 
-	./run_stress.py etc/sample-test.json -d 30
+	./run_stress.py etc/server-create-destroy-test.json -d 30
 
 This sample test tries to create a few VMs and kill a few VMs.
 
diff --git a/tempest/stress/actions/create_destroy_server.py b/tempest/stress/actions/server_create_destroy.py
similarity index 96%
rename from tempest/stress/actions/create_destroy_server.py
rename to tempest/stress/actions/server_create_destroy.py
index 68dc148..1a1e30b 100644
--- a/tempest/stress/actions/create_destroy_server.py
+++ b/tempest/stress/actions/server_create_destroy.py
@@ -16,7 +16,7 @@
 import tempest.stress.stressaction as stressaction
 
 
-class CreateDestroyServerTest(stressaction.StressAction):
+class ServerCreateDestroyTest(stressaction.StressAction):
 
     def setUp(self, **kwargs):
         self.image = self.manager.config.compute.image_ref
diff --git a/tempest/stress/actions/volume_attach_delete.py b/tempest/stress/actions/volume_attach_delete.py
new file mode 100644
index 0000000..a7b872f
--- /dev/null
+++ b/tempest/stress/actions/volume_attach_delete.py
@@ -0,0 +1,70 @@
+# (c) 2013 Deutsche Telekom AG
+#    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
+import tempest.stress.stressaction as stressaction
+
+
+class VolumeAttachDeleteTest(stressaction.StressAction):
+
+    def setUp(self, **kwargs):
+        self.image = self.manager.config.compute.image_ref
+        self.flavor = self.manager.config.compute.flavor_ref
+
+    def run(self):
+        # Step 1: create volume
+        name = rand_name("volume")
+        self.logger.info("creating volume: %s" % name)
+        resp, volume = self.manager.volumes_client.create_volume(size=1,
+                                                                 display_name=
+                                                                 name)
+        assert(resp.status == 200)
+        self.manager.volumes_client.wait_for_volume_status(volume['id'],
+                                                           'available')
+        self.logger.info("created volume: %s" % volume['id'])
+
+        # Step 2: create vm instance
+        vm_name = rand_name("instance")
+        self.logger.info("creating vm: %s" % vm_name)
+        resp, server = self.manager.servers_client.create_server(
+            vm_name, self.image, self.flavor)
+        server_id = server['id']
+        assert(resp.status == 202)
+        self.manager.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+        self.logger.info("created vm %s" % server_id)
+
+        # Step 3: attach volume to vm
+        self.logger.info("attach volume (%s) to vm %s" %
+                        (volume['id'], server_id))
+        resp, body = self.manager.servers_client.attach_volume(server_id,
+                                                               volume['id'],
+                                                               '/dev/vdc')
+        assert(resp.status == 200)
+        self.manager.volumes_client.wait_for_volume_status(volume['id'],
+                                                           'in-use')
+        self.logger.info("volume (%s) attached to vm %s" %
+                         (volume['id'], server_id))
+
+        # Step 4: delete vm
+        self.logger.info("deleting vm: %s" % vm_name)
+        resp, _ = self.manager.servers_client.delete_server(server_id)
+        assert(resp.status == 204)
+        self.manager.servers_client.wait_for_server_termination(server_id)
+        self.logger.info("deleted vm: %s" % server_id)
+
+        # Step 5: delete volume
+        self.logger.info("deleting volume: %s" % volume['id'])
+        resp, _ = self.manager.volumes_client.delete_volume(volume['id'])
+        assert(resp.status == 202)
+        self.manager.volumes_client.wait_for_resource_deletion(volume['id'])
+        self.logger.info("deleted volume: %s" % volume['id'])
diff --git a/tempest/stress/actions/volume_create_delete.py b/tempest/stress/actions/volume_create_delete.py
index 184f870..e29d9c4 100644
--- a/tempest/stress/actions/volume_create_delete.py
+++ b/tempest/stress/actions/volume_create_delete.py
@@ -14,20 +14,20 @@
 import tempest.stress.stressaction as stressaction
 
 
-class CreateDeleteTest(stressaction.StressAction):
+class VolumeCreateDeleteTest(stressaction.StressAction):
 
     def run(self):
         name = rand_name("volume")
         self.logger.info("creating %s" % name)
-        resp, volume = self.manager.volumes_client.\
-            create_volume(size=1, display_name=name)
+        volumes_client = self.manager.volumes_client
+        resp, volume = volumes_client.create_volume(size=1,
+                                                    display_name=name)
         assert(resp.status == 200)
         vol_id = volume['id']
-        status = 'available'
-        self.manager.volumes_client.wait_for_volume_status(vol_id, status)
+        volumes_client.wait_for_volume_status(vol_id, 'available')
         self.logger.info("created %s" % volume['id'])
         self.logger.info("deleting %s" % name)
-        resp, _ = self.manager.volumes_client.delete_volume(vol_id)
+        resp, _ = volumes_client.delete_volume(vol_id)
         assert(resp.status == 202)
-        self.manager.volumes_client.wait_for_resource_deletion(vol_id)
+        volumes_client.wait_for_resource_deletion(vol_id)
         self.logger.info("deleted %s" % vol_id)
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index d170eb8..d9b95e0 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -12,15 +12,16 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
-import importlib
 import logging
 import multiprocessing
+import signal
 import time
 
 from tempest import clients
 from tempest.common import ssh
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
+from tempest.openstack.common import importutils
 from tempest.stress import cleanup
 
 admin_manager = clients.AdminManager()
@@ -45,6 +46,7 @@
 # add the handler to the root logger
 logger = logging.getLogger('tempest.stress')
 logger.addHandler(_console)
+processes = []
 
 
 def do_ssh(command, host):
@@ -93,15 +95,29 @@
     return None
 
 
-def get_action_object(path):
-    (module_part, _, obj_name) = path.rpartition('.')
-    return getattr(importlib.import_module(module_part), obj_name)
+def sigchld_handler(signal, frame):
+    """
+    Signal handler (only active if stop_on_error is True).
+    """
+    terminate_all_processes()
 
 
-def stress_openstack(tests, duration, max_runs=None):
+def terminate_all_processes():
+    """
+    Goes through the process list and terminates all child processes.
+    """
+    for process in processes:
+        if process['process'].is_alive():
+            try:
+                process['process'].terminate()
+            except Exception:
+                pass
+        process['process'].join()
+
+
+def stress_openstack(tests, duration, max_runs=None, stop_on_error=False):
     """
     Workload driver. Executes an action function against a nova-cluster.
-
     """
     logfiles = admin_manager.config.stress.target_logfiles
     log_check_interval = int(admin_manager.config.stress.log_check_interval)
@@ -110,7 +126,6 @@
         computes = _get_compute_nodes(controller)
         for node in computes:
             do_ssh("rm -f %s" % logfiles, node)
-    processes = []
     for test in tests:
         if test.get('use_admin', False):
             manager = admin_manager
@@ -131,8 +146,8 @@
                                           password="pass",
                                           tenant_name=tenant_name)
 
-            test_obj = get_action_object(test['action'])
-            test_run = test_obj(manager, logger, max_runs)
+            test_obj = importutils.import_class(test['action'])
+            test_run = test_obj(manager, logger, max_runs, stop_on_error)
 
             kwargs = test.get('kwargs', {})
             test_run.setUp(**dict(kwargs.iteritems()))
@@ -155,6 +170,9 @@
 
             processes.append(process)
             p.start()
+    if stop_on_error:
+        # NOTE(mkoderer): only the parent should register the handler
+        signal.signal(signal.SIGCHLD, sigchld_handler)
     end_time = time.time() + duration
     had_errors = False
     while True:
@@ -173,6 +191,11 @@
                 break
 
         time.sleep(min(remaining, log_check_interval))
+        if stop_on_error:
+            for process in processes:
+                if process['statistic']['fails'] > 0:
+                    break
+
         if not logfiles:
             continue
         errors = _error_in_logs(logfiles, computes)
@@ -180,10 +203,7 @@
             had_errors = True
             break
 
-    for process in processes:
-        if process['process'].is_alive():
-            process['process'].terminate()
-        process['process'].join()
+    terminate_all_processes()
 
     sum_fails = 0
     sum_runs = 0
diff --git a/tempest/stress/etc/sample-test.json b/tempest/stress/etc/sample-test.json
deleted file mode 100644
index 494c823..0000000
--- a/tempest/stress/etc/sample-test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[{"action": "tempest.stress.actions.create_destroy_server.CreateDestroyServerTest",
-  "threads": 8,
-  "use_admin": false,
-  "use_isolated_tenants": false,
-  "kwargs": {}
-  }
-]
diff --git a/tempest/stress/etc/server-create-destroy-test.json b/tempest/stress/etc/server-create-destroy-test.json
new file mode 100644
index 0000000..17d5e1a
--- /dev/null
+++ b/tempest/stress/etc/server-create-destroy-test.json
@@ -0,0 +1,7 @@
+[{"action": "tempest.stress.actions.server_create_destroy.ServerCreateDestroyTest",
+  "threads": 8,
+  "use_admin": false,
+  "use_isolated_tenants": false,
+  "kwargs": {}
+  }
+]
diff --git a/tempest/stress/etc/stress-tox-job.json b/tempest/stress/etc/stress-tox-job.json
index 159794b..dffc469 100644
--- a/tempest/stress/etc/stress-tox-job.json
+++ b/tempest/stress/etc/stress-tox-job.json
@@ -1,13 +1,19 @@
-[{"action": "tempest.stress.actions.create_destroy_server.CreateDestroyServerTest",
+[{"action": "tempest.stress.actions.server_create_destroy.ServerCreateDestroyTest",
   "threads": 8,
   "use_admin": false,
   "use_isolated_tenants": false,
   "kwargs": {}
   },
-  {"action": "tempest.stress.actions.volume_create_delete.CreateDeleteTest",
+  {"action": "tempest.stress.actions.volume_create_delete.VolumeCreateDeleteTest",
   "threads": 4,
   "use_admin": false,
   "use_isolated_tenants": false,
   "kwargs": {}
+  },
+  {"action": "tempest.stress.actions.volume_attach_delete.VolumeAttachDeleteTest",
+  "threads": 2,
+  "use_admin": false,
+  "use_isolated_tenants": false,
+  "kwargs": {}
   }
 ]
diff --git a/tempest/stress/etc/volume-attach-delete-test.json b/tempest/stress/etc/volume-attach-delete-test.json
new file mode 100644
index 0000000..4553ff8
--- /dev/null
+++ b/tempest/stress/etc/volume-attach-delete-test.json
@@ -0,0 +1,7 @@
+[{"action": "tempest.stress.actions.volume_attach_delete.VolumeAttachDeleteTest",
+  "threads": 4,
+  "use_admin": false,
+  "use_isolated_tenants": false,
+  "kwargs": {}
+  }
+]
diff --git a/tempest/stress/etc/volume-create-delete-test.json b/tempest/stress/etc/volume-create-delete-test.json
index 6325bdc..e8a58f7 100644
--- a/tempest/stress/etc/volume-create-delete-test.json
+++ b/tempest/stress/etc/volume-create-delete-test.json
@@ -1,4 +1,4 @@
-[{"action": "tempest.stress.actions.volume_create_delete.CreateDeleteTest",
+[{"action": "tempest.stress.actions.volume_create_delete.VolumeCreateDeleteTest",
   "threads": 4,
   "use_admin": false,
   "use_isolated_tenants": false,
diff --git a/tempest/stress/run_stress.py b/tempest/stress/run_stress.py
index 106049d..32e3ae0 100755
--- a/tempest/stress/run_stress.py
+++ b/tempest/stress/run_stress.py
@@ -22,7 +22,7 @@
 
 
 def main(ns):
-    #NOTE(kodererm): moved import to make "-h" possible without OpenStack
+    # NOTE(mkoderer): moved import to make "-h" possible without OpenStack
     from tempest.stress import driver
     result = 0
     tests = json.load(open(ns.tests, 'r'))
@@ -30,12 +30,13 @@
         for test in tests:
             step_result = driver.stress_openstack([test],
                                                   ns.duration,
-                                                  ns.number)
-            #NOTE(kodererm): we just save the last result code
+                                                  ns.number,
+                                                  ns.stop)
+            # NOTE(mkoderer): we just save the last result code
             if (step_result != 0):
                 result = step_result
     else:
-        driver.stress_openstack(tests, ns.duration, ns.number)
+        driver.stress_openstack(tests, ns.duration, ns.number, ns.stop)
     return result
 
 
@@ -44,6 +45,8 @@
                     help="Duration of test in secs.")
 parser.add_argument('-s', '--serial', action='store_true',
                     help="Trigger running tests serially.")
+parser.add_argument('-S', '--stop', action='store_true',
+                    default=False, help="Stop on first error.")
 parser.add_argument('-n', '--number', type=int,
                     help="How often an action is executed for each process.")
 parser.add_argument('tests', help="Name of the file with test description.")
diff --git a/tempest/stress/stressaction.py b/tempest/stress/stressaction.py
index 77ddd1c..ab09adc 100644
--- a/tempest/stress/stressaction.py
+++ b/tempest/stress/stressaction.py
@@ -20,10 +20,11 @@
 
 class StressAction(object):
 
-    def __init__(self, manager, logger, max_runs=None):
+    def __init__(self, manager, logger, max_runs=None, stop_on_error=False):
         self.manager = manager
         self.logger = logger
         self.max_runs = max_runs
+        self.stop_on_error = stop_on_error
 
     def _shutdown_handler(self, signal, frame):
         self.tearDown()
@@ -63,6 +64,11 @@
                 self.logger.exception("Failure in run")
             finally:
                 shared_statistic['runs'] += 1
+                if self.stop_on_error and (shared_statistic['fails'] > 1):
+                    self.logger.warn("Stop process due to"
+                                     "\"stop-on-error\" argument")
+                    self.tearDown()
+                    sys.exit(1)
 
     def run(self):
         """This method is where the stress test code runs."""
diff --git a/tempest/test.py b/tempest/test.py
index 7ba63cd..6c304c3 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -18,15 +18,16 @@
 import os
 import time
 
+import fixtures
 import nose.plugins.attrib
 import testresources
 import testtools
 
 from tempest import clients
-from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
 from tempest import config
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
 
@@ -104,6 +105,25 @@
         if hasattr(super(BaseTestCase, cls), 'setUpClass'):
             super(BaseTestCase, cls).setUpClass()
 
+    def setUp(cls):
+        super(BaseTestCase, cls).setUp()
+        test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
+        try:
+            test_timeout = int(test_timeout)
+        except ValueError:
+            test_timeout = 0
+        if test_timeout > 0:
+            cls.useFixture(fixtures.Timeout(test_timeout, gentle=True))
+
+        if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
+                os.environ.get('OS_STDOUT_CAPTURE') == '1'):
+            stdout = cls.useFixture(fixtures.StringStream('stdout')).stream
+            cls.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
+        if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
+                os.environ.get('OS_STDERR_CAPTURE') == '1'):
+            stderr = cls.useFixture(fixtures.StringStream('stderr')).stream
+            cls.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
+
     @classmethod
     def _get_identity_admin_client(cls):
         """
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index 9ff628c..ba627e3 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -28,10 +28,10 @@
 import keystoneclient.exceptions
 
 import tempest.clients
-from tempest.common import log as logging
 from tempest.common.utils.file_utils import have_effective_read_access
 import tempest.config
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 import tempest.test
 from tempest.thirdparty.boto.utils.wait import re_search_wait
 from tempest.thirdparty.boto.utils.wait import state_wait
@@ -58,8 +58,9 @@
 
     A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path)
     boto_logger = logging.getLogger('boto')
-    level = boto_logger.level
-    boto_logger.setLevel(orig_logging.CRITICAL)  # suppress logging for these
+    level = boto_logger.logger.level
+    boto_logger.logger.setLevel(orig_logging.CRITICAL)  # suppress logging
+                                                        # for these
 
     def _cred_sub_check(connection_data):
         if not id_matcher.match(connection_data["aws_access_key_id"]):
@@ -99,7 +100,7 @@
     except keystoneclient.exceptions.Unauthorized:
         S3_CAN_CONNECT_ERROR = "AWS credentials not set," +\
                                " faild to get them even by keystoneclient"
-    boto_logger.setLevel(level)
+    boto_logger.logger.setLevel(level)
     return {'A_I_IMAGES_READY': A_I_IMAGES_READY,
             'S3_CAN_CONNECT_ERROR': S3_CAN_CONNECT_ERROR,
             'EC2_CAN_CONNECT_ERROR': EC2_CAN_CONNECT_ERROR}
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index 1201866..df2ff6a 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -19,10 +19,10 @@
 import testtools
 
 from tempest import clients
-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 import exceptions
+from tempest.openstack.common import log as logging
 from tempest.test import attr
 from tempest.thirdparty.boto.test import BotoTestCase
 from tempest.thirdparty.boto.utils.s3 import s3_upload_dir
@@ -88,6 +88,53 @@
                                                            image["image_id"])
 
     @attr(type='smoke')
+    def test_run_idempotent_instances(self):
+        # EC2 run instances idempotently
+
+        def _run_instance(client_token):
+            reservation = self.ec2_client.run_instances(
+                image_id=self.images["ami"]["image_id"],
+                kernel_id=self.images["aki"]["image_id"],
+                ramdisk_id=self.images["ari"]["image_id"],
+                instance_type=self.instance_type,
+                client_token=client_token)
+            rcuk = self.addResourceCleanUp(self.destroy_reservation,
+                                           reservation)
+            return (reservation, rcuk)
+
+        def _terminate_reservation(reservation, rcuk):
+            for instance in reservation.instances:
+                instance.terminate()
+            self.cancelResourceCleanUp(rcuk)
+
+        reservation_1, rcuk_1 = _run_instance('token_1')
+        reservation_2, rcuk_2 = _run_instance('token_2')
+        reservation_1a, rcuk_1a = _run_instance('token_1')
+
+        self.assertIsNotNone(reservation_1)
+        self.assertIsNotNone(reservation_2)
+        self.assertIsNotNone(reservation_1a)
+
+        # same reservation for token_1
+        self.assertEqual(reservation_1.id, reservation_1a.id)
+
+        # Cancel cleanup -- since it's a duplicate, it's
+        # handled by rcuk1
+        self.cancelResourceCleanUp(rcuk_1a)
+
+        _terminate_reservation(reservation_1, rcuk_1)
+        _terminate_reservation(reservation_2, rcuk_2)
+
+        reservation_3, rcuk_3 = _run_instance('token_1')
+        self.assertIsNotNone(reservation_3)
+
+        # make sure we don't get the old reservation back
+        self.assertNotEqual(reservation_1.id, reservation_3.id)
+
+        # clean up
+        _terminate_reservation(reservation_3, rcuk_3)
+
+    @attr(type='smoke')
     def test_run_stop_terminate_instance(self):
         # EC2 run, stop and terminate instance
         image_ami = self.ec2_client.get_image(self.images["ami"]
diff --git a/tempest/thirdparty/boto/test_ec2_volumes.py b/tempest/thirdparty/boto/test_ec2_volumes.py
index c90c586..dbb3104 100644
--- a/tempest/thirdparty/boto/test_ec2_volumes.py
+++ b/tempest/thirdparty/boto/test_ec2_volumes.py
@@ -16,7 +16,7 @@
 #    under the License.
 
 from tempest import clients
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
 from tempest.test import attr
 from tempest.thirdparty.boto.test import BotoTestCase
 
diff --git a/tempest/thirdparty/boto/test_s3_ec2_images.py b/tempest/thirdparty/boto/test_s3_ec2_images.py
index 5e1e2cb..e2ca15f 100644
--- a/tempest/thirdparty/boto/test_s3_ec2_images.py
+++ b/tempest/thirdparty/boto/test_s3_ec2_images.py
@@ -22,7 +22,6 @@
 from tempest.test import attr
 from tempest.thirdparty.boto.test import BotoTestCase
 from tempest.thirdparty.boto.utils.s3 import s3_upload_dir
-from tempest.thirdparty.boto.utils.wait import state_wait
 
 
 class S3ImagesTest(BotoTestCase):
@@ -51,8 +50,6 @@
                                cls.bucket_name)
         s3_upload_dir(bucket, cls.materials_path)
 
-    #Note(afazekas): Without the normal status change test!
-    # otherwise I would skip it too
     @attr(type='smoke')
     def test_register_get_deregister_ami_image(self):
         # Register and deregister ami image
@@ -70,13 +67,8 @@
         retrieved_image = self.images_client.get_image(image["image_id"])
         self.assertTrue(retrieved_image.name == image["name"])
         self.assertTrue(retrieved_image.id == image["image_id"])
-        state = retrieved_image.state
-        if state != "available":
-            def _state():
-                retr = self.images_client.get_image(image["image_id"])
-                return retr.state
-            state = state_wait(_state, "available")
-        self.assertEqual("available", state)
+        if retrieved_image.state != "available":
+            self.assertImageStateWait(retrieved_image, "available")
         self.images_client.deregister_image(image["image_id"])
         self.assertNotIn(image["image_id"], str(
             self.images_client.get_all_images()))
diff --git a/tempest/thirdparty/boto/utils/s3.py b/tempest/thirdparty/boto/utils/s3.py
index a309a12..f8fa61b 100644
--- a/tempest/thirdparty/boto/utils/s3.py
+++ b/tempest/thirdparty/boto/utils/s3.py
@@ -22,7 +22,7 @@
 import boto
 import boto.s3.key
 
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/thirdparty/boto/utils/wait.py b/tempest/thirdparty/boto/utils/wait.py
index 6b3ef27..d8fca3b 100644
--- a/tempest/thirdparty/boto/utils/wait.py
+++ b/tempest/thirdparty/boto/utils/wait.py
@@ -21,8 +21,8 @@
 import boto.exception
 from testtools import TestCase
 
-from tempest.common import log as logging
 import tempest.config
+from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/whitebox/manager.py b/tempest/whitebox/manager.py
index 471d8b4..b2632f1 100644
--- a/tempest/whitebox/manager.py
+++ b/tempest/whitebox/manager.py
@@ -21,10 +21,11 @@
 import sys
 
 from sqlalchemy import create_engine, MetaData
-from tempest.common import log as logging
+
 from tempest.common.ssh import Client
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
+from tempest.openstack.common import log as logging
 from tempest.scenario import manager
 
 LOG = logging.getLogger(__name__)
diff --git a/tools/pretty_tox_serial.sh b/tools/pretty_tox_serial.sh
new file mode 100755
index 0000000..490d263
--- /dev/null
+++ b/tools/pretty_tox_serial.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+TESTRARGS=$@
+
+if [ ! -d .testrepository ]; then
+    testr init
+fi
+testr run --subunit $TESTRARGS | subunit-2to1 | tools/colorizer.py
+testr slowest
diff --git a/tox.ini b/tox.ini
index 93a53ac..7eae948 100644
--- a/tox.ini
+++ b/tox.ini
@@ -26,6 +26,12 @@
 commands =
   nosetests --logging-format '%(asctime)-15s %(message)s' --with-xunit --xunit-file=nosetests-full.xml -sv tempest/api tempest/scenario tempest/thirdparty tempest/cli
 
+[testenv:testr-serial]
+sitepackages = True
+setenv = VIRTUAL_ENV={envdir}
+commands =
+  sh tools/pretty_tox_serial.sh '{posargs} tempest.api tempest.scenario tempest.thirdparty tempest.cli'
+
 [testenv:testr-full]
 sitepackages = True
 setenv = VIRTUAL_ENV={envdir}