Merge "Fix stress test README.rst"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index f7f74b8..48ca905 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -494,7 +494,7 @@
 
 # Matching flavors become parameters for scenario tests
 # (string value)
-#flavor_regex=^m1.(micro|nano|tiny)$
+#flavor_regex=^m1.nano$
 
 # SSH verification in tests is skippedfor matching images
 # (string value)
diff --git a/requirements.txt b/requirements.txt
index 3b3e1fa..8c0f872 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,22 +1,22 @@
 pbr>=0.5.21,<1.0
 anyjson>=0.3.3
 nose
-httplib2
-jsonschema>=1.3.0,!=1.4.0
-testtools>=0.9.32
+httplib2>=0.7.5
+jsonschema>=2.0.0,<3.0.0
+testtools>=0.9.34
 lxml>=2.3
 boto>=2.12.0,!=2.13.0
-paramiko>=1.8.0
+paramiko>=1.9.0
 netaddr>=0.7.6
 python-glanceclient>=0.9.0
-python-keystoneclient>=0.4.1
+python-keystoneclient>=0.4.2
 python-novaclient>=2.15.0
-python-neutronclient>=2.3.0,<3
+python-neutronclient>=2.3.3,<3
 python-cinderclient>=1.0.6
 python-heatclient>=0.2.3
 python-swiftclient>=1.5
 testresources>=0.2.4
-keyring>=1.6.1,<2.0
+keyring>=1.6.1,<2.0,>=2.1
 testrepository>=0.0.17
 oslo.config>=1.2.0
 six>=1.4.1
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 441c87d..f8b0051 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -229,6 +229,18 @@
         cls.password = server['adminPass']
         return server['id']
 
+    @classmethod
+    def delete_volume(cls, volume_id):
+        """Deletes the given volume and waits for it to be gone."""
+        try:
+            resp, _ = cls.volumes_extensions_client.delete_volume(volume_id)
+            # TODO(mriedem): We should move the wait_for_resource_deletion
+            # into the delete_volume method as a convenience to the caller.
+            cls.volumes_extensions_client.wait_for_resource_deletion(volume_id)
+        except exceptions.NotFound:
+            LOG.warn("Unable to delete volume '%s' since it was not found. "
+                     "Maybe it was already deleted?" % volume_id)
+
 
 class BaseV2ComputeAdminTest(BaseV2ComputeTest):
     """Base test case class for Compute Admin V2 API tests."""
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index d12f91c..e52ea4a 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -1,5 +1,3 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
 # Copyright 2012 OpenStack Foundation
 # All Rights Reserved.
 #
diff --git a/tempest/api/compute/v3/servers/test_server_metadata.py b/tempest/api/compute/v3/servers/test_server_metadata.py
index 1758b0b..00549eb 100644
--- a/tempest/api/compute/v3/servers/test_server_metadata.py
+++ b/tempest/api/compute/v3/servers/test_server_metadata.py
@@ -14,8 +14,7 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest import exceptions
-from tempest.test import attr
+from tempest import test
 
 
 class ServerMetadataV3TestJSON(base.BaseV3ComputeTest):
@@ -40,7 +39,7 @@
         resp, _ = self.client.set_server_metadata(self.server_id, meta)
         self.assertEqual(resp.status, 200)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_list_server_metadata(self):
         # All metadata key/value pairs for a server should be returned
         resp, resp_metadata = self.client.list_server_metadata(self.server_id)
@@ -50,7 +49,7 @@
         expected = {'key1': 'value1', 'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_set_server_metadata(self):
         # The server's metadata should be replaced with the provided values
         # Create a new set of metadata for the server
@@ -64,22 +63,7 @@
         resp, resp_metadata = self.client.list_server_metadata(self.server_id)
         self.assertEqual(resp_metadata, req_metadata)
 
-    @attr(type='gate')
-    def test_server_create_metadata_key_too_long(self):
-        # Attempt to start a server with a meta-data key that is > 255
-        # characters
-
-        # Try a few values
-        for sz in [256, 257, 511, 1023]:
-            key = "k" * sz
-            meta = {key: 'data1'}
-            self.assertRaises(exceptions.OverLimit,
-                              self.create_test_server,
-                              meta=meta)
-
-        # no teardown - all creates should fail
-
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_update_server_metadata(self):
         # The server's metadata values should be updated to the
         # provided values
@@ -93,7 +77,7 @@
         expected = {'key1': 'alt1', 'key2': 'value2', 'key3': 'value3'}
         self.assertEqual(expected, resp_metadata)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_update_metadata_empty_body(self):
         # The original metadata should not be lost if empty metadata body is
         # passed
@@ -103,14 +87,14 @@
         expected = {'key1': 'value1', 'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_get_server_metadata_item(self):
         # The value for a specific metadata key should be returned
         resp, meta = self.client.get_server_metadata_item(self.server_id,
                                                           'key2')
         self.assertEqual('value2', meta['key2'])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_set_server_metadata_item(self):
         # The item's value should be updated to the provided value
         # Update the metadata value
@@ -124,7 +108,7 @@
         expected = {'key1': 'value1', 'key2': 'value2', 'nova': 'alt'}
         self.assertEqual(expected, resp_metadata)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_delete_server_metadata_item(self):
         # The metadata value/key pair should be deleted from the server
         resp, meta = self.client.delete_server_metadata_item(self.server_id,
@@ -135,77 +119,3 @@
         resp, resp_metadata = self.client.list_server_metadata(self.server_id)
         expected = {'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
-
-    @attr(type=['negative', 'gate'])
-    def test_server_metadata_negative(self):
-        # Blank key should trigger an error.
-        meta = {'': 'data1'}
-        self.assertRaises(exceptions.BadRequest,
-                          self.create_test_server,
-                          meta=meta)
-
-        # GET on a non-existent server should not succeed
-        self.assertRaises(exceptions.NotFound,
-                          self.client.get_server_metadata_item, 999, 'test2')
-
-        # List metadata on a non-existent server should not succeed
-        self.assertRaises(exceptions.NotFound,
-                          self.client.list_server_metadata, 999)
-
-        # Raise BadRequest if key in uri does not match
-        # the key passed in body.
-        meta = {'testkey': 'testvalue'}
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.set_server_metadata_item,
-                          self.server_id, 'key', meta)
-
-        # Set metadata on a non-existent server should not succeed
-        meta = {'meta1': 'data1'}
-        self.assertRaises(exceptions.NotFound,
-                          self.client.set_server_metadata, 999, meta)
-
-        # An update should not happen for a non-existent image
-        meta = {'key1': 'value1', 'key2': 'value2'}
-        self.assertRaises(exceptions.NotFound,
-                          self.client.update_server_metadata, 999, meta)
-
-        # Blank key should trigger an error
-        meta = {'': 'data1'}
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.update_server_metadata,
-                          self.server_id, meta=meta)
-
-        # Should not be able to delete metadata item from a non-existent server
-        self.assertRaises(exceptions.NotFound,
-                          self.client.delete_server_metadata_item, 999, 'd')
-
-        # Raise a 413 OverLimit exception while exceeding metadata items limit
-        # for tenant.
-        _, quota_set = self.quotas.get_quota_set(self.tenant_id)
-        quota_metadata = quota_set['metadata_items']
-        req_metadata = {}
-        for num in range(1, quota_metadata + 2):
-            req_metadata['key' + str(num)] = 'val' + str(num)
-        self.assertRaises(exceptions.OverLimit,
-                          self.client.set_server_metadata,
-                          self.server_id, req_metadata)
-
-        # Raise a 413 OverLimit exception while exceeding metadata items limit
-        # for tenant (update).
-        self.assertRaises(exceptions.OverLimit,
-                          self.client.update_server_metadata,
-                          self.server_id, req_metadata)
-
-        # Raise a bad request error for blank key.
-        # set_server_metadata will replace all metadata with new value
-        meta = {'': 'data1'}
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.set_server_metadata,
-                          self.server_id, meta=meta)
-
-        # Raise a bad request error for a missing metadata field
-        # set_server_metadata will replace all metadata with new value
-        meta = {'meta1': 'data1'}
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.set_server_metadata,
-                          self.server_id, meta=meta, no_metadata_field=True)
diff --git a/tempest/api/compute/v3/servers/test_server_metadata_negative.py b/tempest/api/compute/v3/servers/test_server_metadata_negative.py
new file mode 100644
index 0000000..4062696
--- /dev/null
+++ b/tempest/api/compute/v3/servers/test_server_metadata_negative.py
@@ -0,0 +1,158 @@
+# Copyright 2014 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest import test
+
+
+class ServerMetadataV3NegativeTestJSON(base.BaseV3ComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(ServerMetadataV3NegativeTestJSON, cls).setUpClass()
+        cls.client = cls.servers_client
+        cls.quotas = cls.quotas_client
+        cls.admin_client = cls._get_identity_admin_client()
+        resp, tenants = cls.admin_client.list_tenants()
+        cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
+                         cls.client.tenant_name][0]
+        resp, server = cls.create_test_server(meta={}, wait_until='ACTIVE')
+
+        cls.server_id = server['id']
+
+    @test.attr(type=['gate', 'negative'])
+    def test_server_create_metadata_key_too_long(self):
+        # Attempt to start a server with a meta-data key that is > 255
+        # characters
+
+        # Tryset_server_metadata_item a few values
+        for sz in [256, 257, 511, 1023]:
+            key = "k" * sz
+            meta = {key: 'data1'}
+            self.assertRaises(exceptions.OverLimit,
+                              self.create_test_server,
+                              meta=meta)
+
+        # no teardown - all creates should fail
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_server_metadata_blank_key(self):
+        # Blank key should trigger an error.
+        meta = {'': 'data1'}
+        self.assertRaises(exceptions.BadRequest,
+                          self.create_test_server,
+                          meta=meta)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_server_metadata_non_existent_server(self):
+        # GET on a non-existent server should not succeed
+        non_existent_server_id = data_utils.rand_uuid()
+        self.assertRaises(exceptions.NotFound,
+                          self.client.get_server_metadata_item,
+                          non_existent_server_id,
+                          'test2')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_list_server_metadata_non_existent_server(self):
+        # List metadata on a non-existent server should not succeed
+        non_existent_server_id = data_utils.rand_uuid()
+        self.assertRaises(exceptions.NotFound,
+                          self.client.list_server_metadata,
+                          non_existent_server_id)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_wrong_key_passed_in_body(self):
+        # Raise BadRequest if key in uri does not match
+        # the key passed in body.
+        meta = {'testkey': 'testvalue'}
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.set_server_metadata_item,
+                          self.server_id, 'key', meta)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_set_metadata_non_existent_server(self):
+        # Set metadata on a non-existent server should not succeed
+        non_existent_server_id = data_utils.rand_uuid()
+        meta = {'meta1': 'data1'}
+        self.assertRaises(exceptions.NotFound,
+                          self.client.set_server_metadata,
+                          non_existent_server_id,
+                          meta)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_update_metadata_non_existent_server(self):
+        # An update should not happen for a non-existent server
+        non_existent_server_id = data_utils.rand_uuid()
+        meta = {'key1': 'value1', 'key2': 'value2'}
+        self.assertRaises(exceptions.NotFound,
+                          self.client.update_server_metadata,
+                          non_existent_server_id,
+                          meta)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_update_metadata_with_blank_key(self):
+        # Blank key should trigger an error
+        meta = {'': 'data1'}
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.update_server_metadata,
+                          self.server_id, meta=meta)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_delete_metadata_non_existent_server(self):
+        # Should not be able to delete metadata item from a non-existent server
+        non_existent_server_id = data_utils.rand_uuid()
+        self.assertRaises(exceptions.NotFound,
+                          self.client.delete_server_metadata_item,
+                          non_existent_server_id,
+                          'd')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_metadata_items_limit(self):
+        # Raise a 413 OverLimit exception while exceeding metadata items limit
+        # for tenant.
+        _, quota_set = self.quotas.get_quota_set(self.tenant_id)
+        quota_metadata = quota_set['metadata_items']
+        req_metadata = {}
+        for num in range(1, quota_metadata + 2):
+            req_metadata['key' + str(num)] = 'val' + str(num)
+        self.assertRaises(exceptions.OverLimit,
+                          self.client.set_server_metadata,
+                          self.server_id, req_metadata)
+
+        # Raise a 413 OverLimit exception while exceeding metadata items limit
+        # for tenant (update).
+        self.assertRaises(exceptions.OverLimit,
+                          self.client.update_server_metadata,
+                          self.server_id, req_metadata)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_set_server_metadata_blank_key(self):
+        # Raise a bad request error for blank key.
+        # set_server_metadata will replace all metadata with new value
+        meta = {'': 'data1'}
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.set_server_metadata,
+                          self.server_id, meta=meta)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_set_server_metadata_missing_metadata(self):
+        # Raise a bad request error for a missing metadata field
+        # set_server_metadata will replace all metadata with new value
+        meta = {'meta1': 'data1'}
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.set_server_metadata,
+                          self.server_id, meta=meta, no_metadata_field=True)
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index 3fc7c55..bcab891 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -44,7 +44,7 @@
         resp, volume = self.client.create_volume(size=1,
                                                  display_name=v_name,
                                                  metadata=metadata)
-        self.addCleanup(self._delete_volume, volume)
+        self.addCleanup(self.delete_volume, volume['id'])
         self.assertEqual(200, resp.status)
         self.assertIn('id', volume)
         self.assertIn('displayName', volume)
@@ -72,16 +72,6 @@
                         'The fetched Volume metadata misses data '
                         'from the created Volume')
 
-    def _delete_volume(self, volume):
-        # Delete the Volume created in this method
-        try:
-            resp, _ = self.client.delete_volume(volume['id'])
-            self.assertEqual(202, resp.status)
-            # Checking if the deleted Volume still exists
-            self.client.wait_for_resource_deletion(volume['id'])
-        except KeyError:
-            return
-
 
 class VolumesGetTestXML(VolumesGetTestJSON):
     _interface = "xml"
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index bec2ef4..48b1b7e 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -62,7 +62,7 @@
                     # too small. So, here, we clean up whatever we did manage
                     # to create and raise a SkipTest
                     for volume in cls.volume_list:
-                        cls.client.delete_volume(volume)
+                        cls.delete_volume(volume['id'])
                     msg = ("Failed to create ALL necessary volumes to run "
                            "test. This typically means that the backing file "
                            "size of the nova-volumes group is too small to "
@@ -74,8 +74,7 @@
     def tearDownClass(cls):
         # Delete the created Volumes
         for volume in cls.volume_list:
-            resp, _ = cls.client.delete_volume(volume['id'])
-            cls.client.wait_for_resource_deletion(volume['id'])
+            cls.delete_volume(volume['id'])
         super(VolumesTestJSON, cls).tearDownClass()
 
     @attr(type='gate')
diff --git a/tempest/config.py b/tempest/config.py
index 704556f..ccb3125 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -667,7 +667,7 @@
                default='^cirros-0.3.1-x86_64-uec$',
                help="Matching images become parameters for scenario tests"),
     cfg.StrOpt('flavor_regex',
-               default='^m1.(micro|nano|tiny)$',
+               default='^m1.nano$',
                help="Matching flavors become parameters for scenario tests"),
     cfg.StrOpt('non_ssh_image_regex',
                default='^.*[Ww]in.*$',
@@ -727,9 +727,7 @@
 
         path = os.path.join(conf_dir, conf_file)
 
-        if not (os.path.isfile(path) or
-                'TEMPEST_CONFIG_DIR' in os.environ or
-                'TEMPEST_CONFIG' in os.environ):
+        if not os.path.isfile(path):
             path = failsafe_path
 
         # only parse the config file if we expect one to exist. This is needed
diff --git a/tempest/services/compute/xml/common.py b/tempest/services/compute/xml/common.py
index 9c562b7..4def19f 100644
--- a/tempest/services/compute/xml/common.py
+++ b/tempest/services/compute/xml/common.py
@@ -112,11 +112,27 @@
     others, it requires a little hand-editing of the result.
     """
     json = {}
+    bool_flag = False
+    int_flag = False
+    long_flag = False
     for attr in node.keys():
         if not attr.startswith("xmlns"):
             json[attr] = node.get(attr)
+            if json[attr] == 'bool':
+                bool_flag = True
+            elif json[attr] == 'int':
+                int_flag = True
+            elif json[attr] == 'long':
+                long_flag = True
     if not node.getchildren():
-        return node.text or json
+        if bool_flag:
+            return node.text == 'True'
+        elif int_flag:
+            return int(node.text)
+        elif long_flag:
+            return long(node.text)
+        else:
+            return node.text or json
     for child in node.getchildren():
         tag = child.tag
         if tag.startswith("{"):
diff --git a/tempest/test.py b/tempest/test.py
index 5898358..3052f55 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -270,7 +270,7 @@
                                                    level=None))
 
     @classmethod
-    def get_client_manager(cls):
+    def get_client_manager(cls, interface=None):
         """
         Returns an Openstack client manager
         """
@@ -282,12 +282,27 @@
             force_tenant_isolation):
             creds = cls.isolated_creds.get_primary_creds()
             username, tenant_name, password = creds
-            os = clients.Manager(username=username,
-                                 password=password,
-                                 tenant_name=tenant_name,
-                                 interface=cls._interface)
+            if getattr(cls, '_interface', None):
+                os = clients.Manager(username=username,
+                                     password=password,
+                                     tenant_name=tenant_name,
+                                     interface=cls._interface)
+            elif interface:
+                os = clients.Manager(username=username,
+                                     password=password,
+                                     tenant_name=tenant_name,
+                                     interface=interface)
+            else:
+                os = clients.Manager(username=username,
+                                     password=password,
+                                     tenant_name=tenant_name)
         else:
-            os = clients.Manager(interface=cls._interface)
+            if getattr(cls, '_interface', None):
+                os = clients.Manager(interface=cls._interface)
+            elif interface:
+                os = clients.Manager(interface=interface)
+            else:
+                os = clients.Manager()
         return os
 
     @classmethod
diff --git a/tempest/tests/test_compute_xml_common.py b/tempest/tests/test_compute_xml_common.py
new file mode 100644
index 0000000..bfa6a10
--- /dev/null
+++ b/tempest/tests/test_compute_xml_common.py
@@ -0,0 +1,67 @@
+# Copyright 2013 IBM Corp.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+from lxml import etree
+
+from tempest.services.compute.xml import common
+from tempest.tests import base
+
+
+class TestXMLParser(base.TestCase):
+
+    def test_xml_to_json_parser_bool_value(self):
+        node = etree.fromstring('''<health_monitor
+        xmlns="http://openstack.org/quantum/api/v2.0"
+         xmlns:quantum="http://openstack.org/quantum/api/v2.0"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+           <admin_state_up quantum:type="bool">False</admin_state_up>
+          <fake_state_up quantum:type="bool">True</fake_state_up>
+          </health_monitor>''')
+        body = common.xml_to_json(node)
+        self.assertEqual(body['admin_state_up'], False)
+        self.assertEqual(body['fake_state_up'], True)
+
+    def test_xml_to_json_parser_int_value(self):
+        node = etree.fromstring('''<health_monitor
+        xmlns="http://openstack.org/quantum/api/v2.0"
+         xmlns:quantum="http://openstack.org/quantum/api/v2.0"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+          <delay quantum:type="long">4</delay>
+          <max_retries quantum:type="int">3</max_retries>
+          </health_monitor>''')
+        body = common.xml_to_json(node)
+        self.assertEqual(body['delay'], 4L)
+        self.assertEqual(body['max_retries'], 3)
+
+    def test_xml_to_json_parser_text_value(self):
+        node = etree.fromstring('''<health_monitor
+        xmlns="http://openstack.org/quantum/api/v2.0"
+         xmlns:quantum="http://openstack.org/quantum/api/v2.0"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+          <status>ACTIVE</status>
+          </health_monitor>''')
+        body = common.xml_to_json(node)
+        self.assertEqual(body['status'], 'ACTIVE')
+
+    def test_xml_to_json_parser_list_as_value(self):
+        node = etree.fromstring('''<health_monitor
+        xmlns="http://openstack.org/quantum/api/v2.0"
+         xmlns:quantum="http://openstack.org/quantum/api/v2.0"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+          <elements>
+          <element>first_element</element>
+          <element>second_element</element>
+          </elements>
+          </health_monitor>''')
+        body = common.xml_to_json(node, 'elements')
+        self.assertEqual(body['elements'], ['first_element', 'second_element'])
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index d484d94..b36e8c7 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -195,6 +195,7 @@
     def setUpClass(cls):
         super(BotoTestCase, cls).setUpClass()
         cls.conclusion = decision_maker()
+        cls.os = cls.get_client_manager()
         # The trash contains cleanup functions and paramaters in tuples
         # (function, *args, **kwargs)
         cls._resource_trash_bin = {}
@@ -259,6 +260,7 @@
                 LOG.exception("Cleanup failed %s" % func_name)
             finally:
                 del cls._resource_trash_bin[key]
+        cls.clear_isolated_creds()
         super(BotoTestCase, cls).tearDownClass()
         # NOTE(afazekas): let the super called even on exceptions
         # The real exceptions already logged, if the super throws another,
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index 19ddd18..a6932bc 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -15,7 +15,6 @@
 
 from boto import exception
 
-from tempest import clients
 from tempest.common.utils import data_utils
 from tempest.common.utils.linux.remote_client import RemoteClient
 from tempest import config
@@ -41,7 +40,6 @@
         if not cls.conclusion['A_I_IMAGES_READY']:
             raise cls.skipException("".join(("EC2 ", cls.__name__,
                                     ": requires ami/aki/ari manifest")))
-        cls.os = clients.Manager()
         cls.s3_client = cls.os.s3_client
         cls.ec2_client = cls.os.ec2api_client
         cls.zone = cls.ec2_client.get_good_zone()
diff --git a/tempest/thirdparty/boto/test_ec2_keys.py b/tempest/thirdparty/boto/test_ec2_keys.py
index b4c1827..329ace3 100644
--- a/tempest/thirdparty/boto/test_ec2_keys.py
+++ b/tempest/thirdparty/boto/test_ec2_keys.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import clients
 from tempest.common.utils import data_utils
 from tempest.test import attr
 from tempest.test import skip_because
@@ -30,7 +29,6 @@
     @classmethod
     def setUpClass(cls):
         super(EC2KeysTest, cls).setUpClass()
-        cls.os = clients.Manager()
         cls.client = cls.os.ec2api_client
         cls.ec = cls.ec2_error_code
 
diff --git a/tempest/thirdparty/boto/test_ec2_network.py b/tempest/thirdparty/boto/test_ec2_network.py
index 3c3c74d..4b2f01f 100644
--- a/tempest/thirdparty/boto/test_ec2_network.py
+++ b/tempest/thirdparty/boto/test_ec2_network.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import clients
 from tempest.test import attr
 from tempest.test import skip_because
 from tempest.thirdparty.boto.test import BotoTestCase
@@ -24,7 +23,6 @@
     @classmethod
     def setUpClass(cls):
         super(EC2NetworkTest, cls).setUpClass()
-        cls.os = clients.Manager()
         cls.client = cls.os.ec2api_client
 
     # Note(afazekas): these tests for things duable without an instance
diff --git a/tempest/thirdparty/boto/test_ec2_security_groups.py b/tempest/thirdparty/boto/test_ec2_security_groups.py
index 75dd254..9b58603 100644
--- a/tempest/thirdparty/boto/test_ec2_security_groups.py
+++ b/tempest/thirdparty/boto/test_ec2_security_groups.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import clients
 from tempest.common.utils import data_utils
 from tempest.test import attr
 from tempest.thirdparty.boto.test import BotoTestCase
@@ -24,7 +23,6 @@
     @classmethod
     def setUpClass(cls):
         super(EC2SecurityGroupTest, cls).setUpClass()
-        cls.os = clients.Manager()
         cls.client = cls.os.ec2api_client
 
     @attr(type='smoke')
diff --git a/tempest/thirdparty/boto/test_ec2_volumes.py b/tempest/thirdparty/boto/test_ec2_volumes.py
index 3e7e2de..04671c5 100644
--- a/tempest/thirdparty/boto/test_ec2_volumes.py
+++ b/tempest/thirdparty/boto/test_ec2_volumes.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import clients
 from tempest.openstack.common import log as logging
 from tempest.test import attr
 from tempest.thirdparty.boto.test import BotoTestCase
@@ -31,7 +30,6 @@
     @classmethod
     def setUpClass(cls):
         super(EC2VolumesTest, cls).setUpClass()
-        cls.os = clients.Manager()
         cls.client = cls.os.ec2api_client
         cls.zone = cls.client.get_good_zone()
 
diff --git a/tempest/thirdparty/boto/test_s3_buckets.py b/tempest/thirdparty/boto/test_s3_buckets.py
index f8948fd..f34faac 100644
--- a/tempest/thirdparty/boto/test_s3_buckets.py
+++ b/tempest/thirdparty/boto/test_s3_buckets.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import clients
 from tempest.common.utils import data_utils
 from tempest.test import attr
 from tempest.test import skip_because
@@ -25,7 +24,6 @@
     @classmethod
     def setUpClass(cls):
         super(S3BucketsTest, cls).setUpClass()
-        cls.os = clients.Manager()
         cls.client = cls.os.s3_client
 
     @skip_because(bug="1076965")
diff --git a/tempest/thirdparty/boto/test_s3_ec2_images.py b/tempest/thirdparty/boto/test_s3_ec2_images.py
index 722577b..9607a92 100644
--- a/tempest/thirdparty/boto/test_s3_ec2_images.py
+++ b/tempest/thirdparty/boto/test_s3_ec2_images.py
@@ -15,7 +15,6 @@
 
 import os
 
-from tempest import clients
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest.test import attr
@@ -33,7 +32,6 @@
         if not cls.conclusion['A_I_IMAGES_READY']:
             raise cls.skipException("".join(("EC2 ", cls.__name__,
                                     ": requires ami/aki/ari manifest")))
-        cls.os = clients.Manager()
         cls.s3_client = cls.os.s3_client
         cls.images_client = cls.os.ec2api_client
         cls.materials_path = CONF.boto.s3_materials_path
diff --git a/tempest/thirdparty/boto/test_s3_objects.py b/tempest/thirdparty/boto/test_s3_objects.py
index f355899..a102a22 100644
--- a/tempest/thirdparty/boto/test_s3_objects.py
+++ b/tempest/thirdparty/boto/test_s3_objects.py
@@ -17,7 +17,6 @@
 
 import boto.s3.key
 
-from tempest import clients
 from tempest.common.utils import data_utils
 from tempest.test import attr
 from tempest.thirdparty.boto.test import BotoTestCase
@@ -28,7 +27,6 @@
     @classmethod
     def setUpClass(cls):
         super(S3BucketsTest, cls).setUpClass()
-        cls.os = clients.Manager()
         cls.client = cls.os.s3_client
 
     @attr(type='smoke')
diff --git a/test-requirements.txt b/test-requirements.txt
index d7340f3..3fe2f27 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,7 +2,7 @@
 # needed for doc build
 docutils==0.9.1
 sphinx>=1.1.2,<1.2
-python-subunit
+python-subunit>=0.0.18
 oslo.sphinx
 mox>=0.5.3
 mock>=1.0