Merge "Add tox job for serial testr"
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..825965f 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -1,3 +1,7 @@
+[DEFAULT]
+# log_config = /opt/stack/tempest/etc/logging.conf.sample
+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_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/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_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..4860090 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,
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/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/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/driver.py b/tempest/stress/driver.py
index d170eb8..c4c2041 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -12,7 +12,6 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
-import importlib
 import logging
 import multiprocessing
 import time
@@ -21,6 +20,7 @@
 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()
@@ -93,11 +93,6 @@
     return None
 
 
-def get_action_object(path):
-    (module_part, _, obj_name) = path.rpartition('.')
-    return getattr(importlib.import_module(module_part), obj_name)
-
-
 def stress_openstack(tests, duration, max_runs=None):
     """
     Workload driver. Executes an action function against a nova-cluster.
@@ -131,7 +126,7 @@
                                           password="pass",
                                           tenant_name=tenant_name)
 
-            test_obj = get_action_object(test['action'])
+            test_obj = importutils.import_class(test['action'])
             test_run = test_obj(manager, logger, max_runs)
 
             kwargs = test.get('kwargs', {})
diff --git a/tempest/stress/etc/stress-tox-job.json b/tempest/stress/etc/stress-tox-job.json
index 159794b..3534c26 100644
--- a/tempest/stress/etc/stress-tox-job.json
+++ b/tempest/stress/etc/stress-tox-job.json
@@ -9,5 +9,11 @@
   "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-assign-delete-test.json b/tempest/stress/etc/volume-assign-delete-test.json
new file mode 100644
index 0000000..4553ff8
--- /dev/null
+++ b/tempest/stress/etc/volume-assign-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/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 07abaaa..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
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__)