diff --git a/oslo/messaging/_drivers/impl_rabbit.py b/oslo/messaging/_drivers/impl_rabbit.py
index 2d43e43d7..67de80e35 100644
--- a/oslo/messaging/_drivers/impl_rabbit.py
+++ b/oslo/messaging/_drivers/impl_rabbit.py
@@ -606,11 +606,11 @@ class Connection(object):
             try:
                 self._connect(broker)
                 return
-            except IOError as e:
-                pass
-            except self.connection_errors as e:
-                pass
-            except Exception as e:
+            except IOError as ex:
+                e = ex
+            except self.connection_errors as ex:
+                e = ex
+            except Exception as ex:
                 # NOTE(comstud): Unfortunately it's possible for amqplib
                 # to return an error not covered by its transport
                 # connection_errors in the case of a timeout waiting for
@@ -619,6 +619,7 @@ class Connection(object):
                 # and try to reconnect in this case.
                 if 'timeout' not in six.text_type(e):
                     raise
+                e = ex
 
             log_info = {}
             log_info['err_str'] = e
diff --git a/oslo/messaging/target.py b/oslo/messaging/target.py
index c4c7f81d4..f37a2b296 100644
--- a/oslo/messaging/target.py
+++ b/oslo/messaging/target.py
@@ -89,3 +89,6 @@ class Target(object):
                 attrs.append((a, v))
         values = ', '.join(['%s=%s' % i for i in attrs])
         return '<Target ' + values + '>'
+
+    def __hash__(self):
+        return id(self)
diff --git a/requirements-py3.txt b/requirements-py3.txt
new file mode 100644
index 000000000..4612e6fb4
--- /dev/null
+++ b/requirements-py3.txt
@@ -0,0 +1,20 @@
+oslo.config>=1.2.1
+stevedore>=0.14
+
+# for timeutils
+iso8601>=0.1.9
+
+# for jsonutils
+six>=1.7.0
+
+# used by openstack/common/gettextutils.py
+Babel>=1.3
+
+# for the routing notifier
+PyYAML>=3.1.0
+
+# rabbit driver is the default
+kombu>=2.4.8
+
+# middleware
+WebOb>=1.2.3
diff --git a/test-requirements-py3.txt b/test-requirements-py3.txt
new file mode 100644
index 000000000..1c922e795
--- /dev/null
+++ b/test-requirements-py3.txt
@@ -0,0 +1,21 @@
+# Hacking already pins down pep8, pyflakes and flake8
+hacking>=0.9.1,<0.10
+
+discover
+fixtures>=0.3.14
+mock>=1.0
+mox3>=0.7.0
+python-subunit>=0.0.18
+testrepository>=0.0.18
+testscenarios>=0.4
+testtools>=0.9.34
+oslotest
+
+# when we can require tox>= 1.4, this can go into tox.ini:
+#  [testenv:cover]
+#  deps = {[testenv]deps} coverage
+coverage>=3.6
+
+# this is required for the docs build jobs
+sphinx>=1.1.2,!=1.2.0,<1.3
+oslosphinx
diff --git a/tests/drivers/test_impl_qpid.py b/tests/drivers/test_impl_qpid.py
index 1af716286..e648d972d 100644
--- a/tests/drivers/test_impl_qpid.py
+++ b/tests/drivers/test_impl_qpid.py
@@ -14,12 +14,16 @@
 
 import operator
 import random
-import thread
 import threading
 import time
+import unittest
 
 import mock
-import qpid
+try:
+    import qpid
+except ImportError:
+    raise unittest.SkipTest("qpid not available")
+from six.moves import _thread
 import testscenarios
 
 from oslo import messaging
@@ -368,7 +372,7 @@ class TestQpidTopicAndFanout(_QpidBaseTestCase):
             msgcontent = msg
 
         splitmsg = msgcontent.split('-')
-        key = thread.get_ident()
+        key = _thread.get_ident()
 
         if key not in self._messages:
             self._messages[key] = dict()
diff --git a/tests/executors/test_executor.py b/tests/executors/test_executor.py
index 31b2e374f..3c04174f7 100644
--- a/tests/executors/test_executor.py
+++ b/tests/executors/test_executor.py
@@ -16,8 +16,12 @@
 
 import contextlib
 import threading
+import unittest
 
-import eventlet
+try:
+    import eventlet
+except ImportError:
+    raise unittest.SkipTest("Eventlet not available")
 import mock
 import testscenarios
 
diff --git a/tests/notify/test_notifier.py b/tests/notify/test_notifier.py
index 9fda8062c..21c540efa 100644
--- a/tests/notify/test_notifier.py
+++ b/tests/notify/test_notifier.py
@@ -382,7 +382,7 @@ group_2:
                             return_value=self._fake_extension_manager(
                                 mock.MagicMock())):
                 self.router._load_notifiers()
-                groups = self.router.routing_groups.keys()
+                groups = list(self.router.routing_groups.keys())
                 groups.sort()
                 self.assertEqual(['group_1', 'group_2'], groups)
 
diff --git a/tests/rpc/test_server.py b/tests/rpc/test_server.py
index 4c1396ab1..6ba84a1a5 100644
--- a/tests/rpc/test_server.py
+++ b/tests/rpc/test_server.py
@@ -287,7 +287,7 @@ class TestRPCServer(test_utils.BaseTestCase, ServerSetupMixin):
             client.call({}, 'ping', arg='foo')
         except Exception as ex:
             self.assertIsInstance(ex, ValueError)
-            self.assertEqual('dsfoo', ex[0])
+            self.assertEqual('dsfoo', str(ex))
         else:
             self.assertTrue(False)
 
@@ -308,7 +308,7 @@ class TestRPCServer(test_utils.BaseTestCase, ServerSetupMixin):
             client.call({}, 'ping', arg='foo')
         except Exception as ex:
             self.assertIsInstance(ex, ValueError)
-            self.assertEqual('dsfoo', ex[0])
+            self.assertEqual('dsfoo', str(ex))
         else:
             self.assertTrue(False)
 
diff --git a/tests/test_opts.py b/tests/test_opts.py
index 759e2e93a..b3598c551 100644
--- a/tests/test_opts.py
+++ b/tests/test_opts.py
@@ -12,10 +12,16 @@
 #    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 unittest
 
 import pkg_resources
 
-from oslo.messaging import opts
+try:
+    from oslo.messaging import opts
+except ImportError:
+    import six
+    if six.PY3:
+        raise unittest.SkipTest
 from tests import utils as test_utils
 
 
diff --git a/tox.ini b/tox.ini
index 24a10bb78..4146e7df8 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py26,py27,pep8
+envlist = py33,py26,py27,pep8
 
 [testenv]
 setenv =
@@ -23,6 +23,10 @@ commands = {posargs}
 [testenv:docs]
 commands = python setup.py build_sphinx
 
+[testenv:py33]
+deps = -r{toxinidir}/requirements-py3.txt
+       -r{toxinidir}/test-requirements-py3.txt
+
 [flake8]
 show-source = True
 ignore = H237,H402,H405,H904
@@ -32,3 +36,4 @@ builtins = _
 [hacking]
 import_exceptions =
   oslo.messaging.openstack.common.gettextutils
+  six.moves