Merge "Sync fixtures from oslo and use LockFixture"
diff --git a/doc/source/conf.py b/doc/source/conf.py
index cf838c0..e5444ae 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -51,17 +51,6 @@
 project = u'Tempest'
 copyright = u'2013, OpenStack QA Team'
 
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-import pbr.version
-version_info = pbr.version.VersionInfo('tempest')
-version = version_info.version_string()
-# The full version, including alpha/beta/rc tags.
-release = version_info.release_string()
-
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
 #language = None
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 115a2b5..87bf758 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -2,7 +2,7 @@
 #log_config = /opt/stack/tempest/etc/logging.conf.sample
 
 # disable logging to the stderr
-use_stderr = False
+use_stderr = false
 
 # log file
 log_file = tempest.log
@@ -23,7 +23,7 @@
 catalog_type = identity
 # Ignore SSL certificate validation failures? Use when in testing
 # environments that have self-signed SSL certs.
-disable_ssl_certificate_validation = False
+disable_ssl_certificate_validation = false
 # URL for where to find the OpenStack Identity API endpoint (Keystone)
 uri = http://127.0.0.1:5000/v2.0/
 # URL for where to find the OpenStack V3 Identity API endpoint (Keystone)
@@ -131,7 +131,7 @@
 ssh_channel_timeout = 60
 
 # Dose the SSH uses Floating IP?
-use_floatingip_for_ssh = True
+use_floatingip_for_ssh = true
 
 # The type of endpoint for a Compute API service. Unless you have a
 # custom Keystone service catalog implementation, you probably want to leave
@@ -143,36 +143,37 @@
 # catalog, the first found one is used.
 #region = RegionOne
 
+# Expected first device name when a volume is attached to an instance
+volume_device_name = vdb
+
+[compute-feature-enabled]
 # Does the Compute API support creation of images?
-create_image_enabled = true
+create_image = true
 
 # For resize to work with libvirt/kvm, one of the following must be true:
-# Single node: allow_resize_to_same_host=True must be set in nova.conf
+# Single node: allow_resize_to_same_host=true must be set in nova.conf
 # Cluster: the 'nova' user must have scp access between cluster nodes
-resize_available = true
+resize = true
 
 # Does the compute API support changing the admin password?
-change_password_available=true
+change_password = false
 
 # Run live migration tests (requires 2 hosts)
-live_migration_available = false
+live_migration = false
 
 # Use block live migration (Otherwise, non-block migration will be
 # performed, which requires XenServer pools in case of using XS)
-use_block_migration_for_live_migration = false
+block_migration_for_live_migration = false
 
 # Supports iSCSI block migration - depends on a XAPI supporting
 # relax-xsm-sr-check
-block_migrate_supports_cinder_iscsi = false
+block_migrate_cinder_iscsi = false
 
 # When set to false, disk config tests are forced to skip
-disk_config_enabled = true
+disk_config = true
 
 # When set to false, flavor extra data tests are forced to skip
-flavor_extra_enabled = true
-
-# Expected first device name when a volume is attached to an instance
-volume_device_name = vdb
+flavor_extra = true
 
 [compute-admin]
 # This should be the username of a user WITH administrative privileges
@@ -197,12 +198,15 @@
 # catalog, the first found one is used.
 #region = RegionOne
 
-# The version of the OpenStack Images API to use
-api_version = 1
-
 # HTTP image to use for glance http image testing
 http_image = http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz
 
+[image-feature-enabled]
+# Is the image api_v1 enabled
+api_v1 = True
+# Is the image api_v2 enabled
+api_v2 = True
+
 [network]
 # This section contains configuration options used when executing tests
 # against the OpenStack Network API.
@@ -258,10 +262,8 @@
 # Number of seconds to time out on waiting for a volume
 # to be available or reach an expected status
 build_timeout = 300
-# Runs Cinder multi-backend tests (requires 2 backends declared in cinder.conf)
-# They must have different volume_backend_name (backend1_name and backend2_name
-# have to be different)
-multi_backend_enabled = false
+# If multi_backend is enabled there must be 2 volume_backend_names (
+# backend1_name and backend2_name) which have to be different)
 backend1_name = BACKEND_1
 backend2_name = BACKEND_2
 # Protocol and vendor of volume backend to target when testing volume-types.
@@ -269,6 +271,10 @@
 storage_protocol = iSCSI
 vendor_name = Open Source
 
+[volume-feature-enabled]
+#Runs Cinder multi-backend tests (requires 2 backends declared in cinder.conf)
+multi_backend = false
+
 [object-storage]
 # This section contains configuration options used when executing tests
 # against the OpenStack Object Storage API.
@@ -291,14 +297,16 @@
 # Number of seconds to wait while looping to check the status of a
 # container to container synchronization
 container_sync_interval = 5
-# Set to True if the Account Quota middleware is enabled
-accounts_quotas_available = True
-# Set to True if the Container Quota middleware is enabled
-container_quotas_available = True
 
 # Set operator role for tests that require creating a container
 operator_role = Member
 
+[object-feature-enabled]
+# Set to True if the Account Quota middleware is enabled
+accounts_quotas = True
+# Set to True if the Container Quota middleware is enabled
+container_quotas = True
+
 [boto]
 # This section contains configuration options used when executing tests
 # with boto.
@@ -403,7 +411,7 @@
 
 [cli]
 # Enable cli tests
-enabled = True
+enabled = true
 # directory where python client binaries are located
 cli_dir = /usr/local/bin
 # Number of seconds to wait on a CLI timeout
@@ -411,19 +419,21 @@
 
 [service_available]
 # Whether or not cinder is expected to be available
-cinder = True
+cinder = true
 # Whether or not neutron is expected to be available
 neutron = false
 # Whether or not glance is expected to be available
-glance = True
+glance = true
 # Whether or not swift is expected to be available
-swift = True
+swift = true
 # Whether or not nova is expected to be available
-nova = True
+nova = true
 # Whether or not Heat is expected to be available
 heat = false
+# Whether or not Ceilometer is expected to be available
+ceilometer = True
 # Whether or not horizon is expected to be available
-horizon = True
+horizon = true
 
 [stress]
 # Maximum number of instances to create during test
@@ -431,8 +441,8 @@
 # Time (in seconds) between log file error checks
 log_check_interval = 60
 # The default number of threads created while stress test
-default_thread_number_per_action=4
+default_thread_number_per_action = 4
 
 [debug]
 # Enable diagnostic commands
-enable = True
+enable = true
diff --git a/requirements.txt b/requirements.txt
index 4dea533..4f6a1d3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,17 +5,17 @@
 jsonschema>=1.3.0,!=1.4.0
 testtools>=0.9.32
 lxml>=2.3
-boto>=2.4.0
+boto>=2.4.0,!=2.13.0
 paramiko>=1.8.0
 netaddr
 python-glanceclient>=0.9.0
-python-keystoneclient>=0.3.0
-python-novaclient>=2.12.0
-python-neutronclient>=2.2.3,<3
-python-cinderclient>=1.0.4
+python-keystoneclient>=0.4.1
+python-novaclient>=2.15.0
+python-neutronclient>=2.3.0,<3
+python-cinderclient>=1.0.6
 python-heatclient>=0.2.3
 testresources>=0.2.4
-keyring>=1.6.1
+keyring>=1.6.1,<2.0
 testrepository>=0.0.17
 oslo.config>=1.2.0
 eventlet>=0.13.0
diff --git a/setup.cfg b/setup.cfg
index 653ca40..23a97ff 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -17,20 +17,7 @@
     Programming Language :: Python :: 2
     Programming Language :: Python :: 2.7
 
-[global]
-setup-hooks =
-    pbr.hooks.setup_hook
-
 [build_sphinx]
 all_files = 1
 build-dir = doc/build
 source-dir = doc/source
-
-[nosetests]
-# NOTE(jkoelker) To run the test suite under nose install the following
-#                coverage http://pypi.python.org/pypi/coverage
-#                openstack-nose https://github.com/openstack-dev/openstack-nose
-verbosity=2
-
-[pbr]
-autodoc_tree_index_modules=true
diff --git a/setup.py b/setup.py
index 59a0090..70c2b3f 100755
--- a/setup.py
+++ b/setup.py
@@ -14,8 +14,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
 import setuptools
 
 setuptools.setup(
-    setup_requires=['d2to1', 'pbr'],
-    d2to1=True)
+    setup_requires=['pbr'],
+    pbr=True)
diff --git a/tempest/api/compute/__init__.py b/tempest/api/compute/__init__.py
index 2c21740..a528754 100644
--- a/tempest/api/compute/__init__.py
+++ b/tempest/api/compute/__init__.py
@@ -22,11 +22,11 @@
 LOG = logging.getLogger(__name__)
 
 CONFIG = config.TempestConfig()
-CREATE_IMAGE_ENABLED = CONFIG.compute.create_image_enabled
-RESIZE_AVAILABLE = CONFIG.compute.resize_available
-CHANGE_PASSWORD_AVAILABLE = CONFIG.compute.change_password_available
-DISK_CONFIG_ENABLED = CONFIG.compute.disk_config_enabled
-FLAVOR_EXTRA_DATA_ENABLED = CONFIG.compute.flavor_extra_enabled
+CREATE_IMAGE_ENABLED = CONFIG.compute_feature_enabled.create_image
+RESIZE_AVAILABLE = CONFIG.compute_feature_enabled.resize
+CHANGE_PASSWORD_AVAILABLE = CONFIG.compute_feature_enabled.change_password
+DISK_CONFIG_ENABLED = CONFIG.compute_feature_enabled.disk_config
+FLAVOR_EXTRA_DATA_ENABLED = CONFIG.compute_feature_enabled.flavor_extra
 MULTI_USER = True
 
 
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index e8acec5..4ff6b07 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -85,7 +85,7 @@
                           aggregates))
 
     @attr(type='gate')
-    def test_aggregate_create_get_details(self):
+    def test_aggregate_create_update_metadata_get_details(self):
         # Create an aggregate and ensure its details are returned.
         aggregate_name = rand_name(self.aggregate_name_prefix)
         resp, aggregate = self.client.create_aggregate(aggregate_name)
@@ -96,6 +96,18 @@
         self.assertEqual(aggregate['name'], body['name'])
         self.assertEqual(aggregate['availability_zone'],
                          body['availability_zone'])
+        self.assertEqual({}, body["metadata"])
+
+        # set the metadata of the aggregate
+        meta = {"key": "value"}
+        resp, body = self.client.set_metadata(aggregate['id'], meta)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(meta, body["metadata"])
+
+        # verify the metadata has been set
+        resp, body = self.client.get_aggregate(aggregate['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(meta, body["metadata"])
 
     @attr(type='gate')
     def test_aggregate_create_update_with_az(self):
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index aa769ba..8e451a0 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -16,8 +16,6 @@
 
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils.data_utils import rand_name
-from tempest import exceptions
 from tempest.test import attr
 
 
@@ -33,7 +31,13 @@
     def setUpClass(cls):
         super(HostsAdminTestJSON, cls).setUpClass()
         cls.client = cls.os_adm.hosts_client
-        cls.non_admin_client = cls.os.hosts_client
+
+    def _get_host_name(self):
+        resp, hosts = self.client.list_hosts()
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(hosts) >= 1)
+        hostname = hosts[0]['host_name']
+        return hostname
 
     @attr(type='gate')
     def test_list_hosts(self):
@@ -53,14 +57,7 @@
         self.assertTrue(len(hosts) >= 1)
         self.assertIn(host, hosts)
 
-    @attr(type='negative')
-    def test_list_hosts_with_non_existent_zone(self):
-        params = {'zone': 'xxx'}
-        resp, hosts = self.client.list_hosts(params)
-        self.assertEqual(0, len(hosts))
-        self.assertEqual(200, resp.status)
-
-    @attr(type='negative')
+    @attr(type='gate')
     def test_list_hosts_with_a_blank_zone(self):
         # If send the request with a blank zone, the request will be successful
         # and it will return all the hosts list
@@ -69,17 +66,18 @@
         self.assertNotEqual(0, len(hosts))
         self.assertEqual(200, resp.status)
 
-    @attr(type=['negative', 'gate'])
-    def test_list_hosts_with_non_admin_user(self):
-        self.assertRaises(exceptions.Unauthorized,
-                          self.non_admin_client.list_hosts)
+    @attr(type='gate')
+    def test_list_hosts_with_nonexistent_zone(self):
+        # If send the request with a nonexistent zone, the request will be
+        # successful and no hosts will be retured
+        params = {'zone': 'xxx'}
+        resp, hosts = self.client.list_hosts(params)
+        self.assertEqual(0, len(hosts))
+        self.assertEqual(200, resp.status)
 
     @attr(type='gate')
     def test_show_host_detail(self):
-        resp, hosts = self.client.list_hosts()
-        self.assertEqual(200, resp.status)
-        self.assertTrue(len(hosts) >= 1)
-        hostname = hosts[0]['host_name']
+        hostname = self._get_host_name()
 
         resp, resources = self.client.show_host_detail(hostname)
         self.assertEqual(200, resp.status)
@@ -92,12 +90,6 @@
         self.assertIsNotNone(host_resource['project'])
         self.assertEqual(hostname, host_resource['host'])
 
-    @attr(type='negative')
-    def test_show_host_detail_with_nonexist_hostname(self):
-        hostname = rand_name('rand_hostname')
-        self.assertRaises(exceptions.NotFound,
-                          self.client.show_host_detail, hostname)
-
 
 class HostsAdminTestXML(HostsAdminTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
new file mode 100644
index 0000000..6b24e72
--- /dev/null
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -0,0 +1,174 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD.
+#
+#    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.test import attr
+
+
+class HostsAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+
+    """
+    Tests hosts API using admin privileges.
+    """
+
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(HostsAdminNegativeTestJSON, cls).setUpClass()
+        cls.client = cls.os_adm.hosts_client
+        cls.non_admin_client = cls.os.hosts_client
+
+    def _get_host_name(self):
+        resp, hosts = self.client.list_hosts()
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(hosts) >= 1)
+        hostname = hosts[0]['host_name']
+        return hostname
+
+    @attr(type=['negative', 'gate'])
+    def test_list_hosts_with_non_admin_user(self):
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.list_hosts)
+
+    @attr(type=['negative', 'gate'])
+    def test_show_host_detail_with_nonexistent_hostname(self):
+        nonexitent_hostname = data_utils.rand_name('rand_hostname')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.show_host_detail, nonexitent_hostname)
+
+    @attr(type=['negative', 'gate'])
+    def test_show_host_detail_with_non_admin_user(self):
+        hostname = self._get_host_name()
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.show_host_detail,
+                          hostname)
+
+    @attr(type=['negative', 'gate'])
+    def test_update_host_with_non_admin_user(self):
+        hostname = self._get_host_name()
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.update_host,
+                          hostname)
+
+    @attr(type=['negative', 'gate'])
+    def test_update_host_with_extra_param(self):
+        # only 'status' and 'maintenance_mode' are the valid params.
+        hostname = self._get_host_name()
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.update_host,
+                          hostname,
+                          status='enable',
+                          maintenance_mode='enable',
+                          param='XXX')
+
+    @attr(type=['negative', 'gate'])
+    def test_update_host_with_invalid_status(self):
+        # 'status' can only be 'enable' or 'disable'
+        hostname = self._get_host_name()
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.update_host,
+                          hostname,
+                          status='invalid',
+                          maintenance_mode='enable')
+
+    @attr(type=['negative', 'gate'])
+    def test_update_host_with_invalid_maintenance_mode(self):
+        # 'maintenance_mode' can only be 'enable' or 'disable'
+        hostname = self._get_host_name()
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.update_host,
+                          hostname,
+                          status='enable',
+                          maintenance_mode='invalid')
+
+    @attr(type=['negative', 'gate'])
+    def test_update_host_without_param(self):
+        # 'status' or 'maintenance_mode' needed for host update
+        hostname = self._get_host_name()
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.update_host,
+                          hostname)
+
+    @attr(type=['negative', 'gate'])
+    def test_update_nonexistent_host(self):
+        nonexitent_hostname = data_utils.rand_name('rand_hostname')
+
+        self.assertRaises(exceptions.NotFound,
+                          self.client.update_host,
+                          nonexitent_hostname,
+                          status='enable',
+                          maintenance_mode='enable')
+
+    @attr(type=['negative', 'gate'])
+    def test_startup_nonexistent_host(self):
+        nonexitent_hostname = data_utils.rand_name('rand_hostname')
+
+        self.assertRaises(exceptions.NotFound,
+                          self.client.startup_host,
+                          nonexitent_hostname)
+
+    @attr(type=['negative', 'gate'])
+    def test_startup_host_with_non_admin_user(self):
+        hostname = self._get_host_name()
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.startup_host,
+                          hostname)
+
+    @attr(type=['negative', 'gate'])
+    def test_shutdown_nonexistent_host(self):
+        nonexitent_hostname = data_utils.rand_name('rand_hostname')
+
+        self.assertRaises(exceptions.NotFound,
+                          self.client.shutdown_host,
+                          nonexitent_hostname)
+
+    @attr(type=['negative', 'gate'])
+    def test_shutdown_host_with_non_admin_user(self):
+        hostname = self._get_host_name()
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.shutdown_host,
+                          hostname)
+
+    @attr(type=['negative', 'gate'])
+    def test_reboot_nonexistent_host(self):
+        nonexitent_hostname = data_utils.rand_name('rand_hostname')
+
+        self.assertRaises(exceptions.NotFound,
+                          self.client.reboot_host,
+                          nonexitent_hostname)
+
+    @attr(type=['negative', 'gate'])
+    def test_reboot_host_with_non_admin_user(self):
+        hostname = self._get_host_name()
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.reboot_host,
+                          hostname)
+
+
+class HostsAdminNegativeTestXML(HostsAdminNegativeTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index 69b8d9c..c6455b5 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -23,7 +23,7 @@
 from tempest.test import attr
 
 
-class HypervisorAdminNegativeTestJSON(base.BaseComputeAdminTest):
+class HypervisorAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
 
     """
     Tests Hypervisors API that require admin privileges
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 37573c4..5679a45 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -19,7 +19,6 @@
 
 from tempest.api import compute
 from tempest import clients
-from tempest.common import isolated_creds
 from tempest.common.utils.data_utils import parse_image_id
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
@@ -42,21 +41,10 @@
         if not cls.config.service_available.nova:
             skip_msg = ("%s skipped as nova is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
-        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
 
-        if (cls.config.compute.allow_tenant_isolation or
-            cls.force_tenant_isolation is True):
-            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)
-        else:
-            os = clients.Manager(interface=cls._interface)
+        os = cls.get_client_manager()
 
         cls.os = os
-
         cls.build_interval = cls.config.compute.build_interval
         cls.build_timeout = cls.config.compute.build_timeout
         cls.ssh_user = cls.config.compute.ssh_user
@@ -113,7 +101,7 @@
     def tearDownClass(cls):
         cls.clear_images()
         cls.clear_servers()
-        cls.isolated_creds.clear_isolated_creds()
+        cls.clear_isolated_creds()
         super(BaseComputeTest, cls).tearDownClass()
 
     @classmethod
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 6f50a02..fc4a5e0 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -32,7 +32,8 @@
 
 class ServerActionsTestJSON(base.BaseV2ComputeTest):
     _interface = 'json'
-    resize_available = tempest.config.TempestConfig().compute.resize_available
+    resize_available = tempest.config.TempestConfig().\
+        compute_feature_enabled.resize
     run_ssh = tempest.config.TempestConfig().compute.run_ssh
 
     def setUp(self):
diff --git a/tempest/api/compute/test_live_block_migration.py b/tempest/api/compute/test_live_block_migration.py
index bb8b372..7f68ab5 100644
--- a/tempest/api/compute/test_live_block_migration.py
+++ b/tempest/api/compute/test_live_block_migration.py
@@ -59,7 +59,8 @@
     def _migrate_server_to(self, server_id, dest_host):
         _resp, body = self.admin_servers_client.live_migrate_server(
             server_id, dest_host,
-            self.config.compute.use_block_migration_for_live_migration)
+            self.config.compute_feature_enabled.
+            block_migration_for_live_migration)
         return body
 
     def _get_host_other_than(self, host):
@@ -97,7 +98,7 @@
             self.volumes_client.wait_for_volume_status(volume_id, 'available')
         self.volumes_client.delete_volume(volume_id)
 
-    @testtools.skipIf(not CONF.compute.live_migration_available,
+    @testtools.skipIf(not CONF.compute_feature_enabled.live_migration,
                       'Live migration not available')
     @attr(type='gate')
     def test_live_block_migration(self):
@@ -112,7 +113,7 @@
         self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
         self.assertEqual(target_host, self._get_host_for_server(server_id))
 
-    @testtools.skipIf(not CONF.compute.live_migration_available,
+    @testtools.skipIf(not CONF.compute_feature_enabled.live_migration,
                       'Live migration not available')
     @attr(type='gate')
     def test_invalid_host_for_migration(self):
@@ -124,10 +125,12 @@
                           server_id, target_host)
         self.assertEqual('ACTIVE', self._get_server_status(server_id))
 
-    @testtools.skipIf(not CONF.compute.live_migration_available or
-                      not CONF.compute.use_block_migration_for_live_migration,
+    @testtools.skipIf(not CONF.compute_feature_enabled.live_migration or not
+                      CONF.compute_feature_enabled.
+                      block_migration_for_live_migration,
                       'Block Live migration not available')
-    @testtools.skipIf(not CONF.compute.block_migrate_supports_cinder_iscsi,
+    @testtools.skipIf(not CONF.compute_feature_enabled.
+                      block_migrate_cinder_iscsi,
                       'Block Live migration not configured for iSCSI')
     @attr(type='gate')
     def test_iscsi_volume(self):
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 4f54a15..ab0cb00 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -74,17 +74,6 @@
         cls.created_images.append(image['id'])
         return resp, image
 
-    @classmethod
-    def _check_version(cls, version):
-        __, versions = cls.client.get_versions()
-        if version == 'v2.0':
-            if 'v2.0' in versions:
-                return True
-        elif version == 'v1.0':
-            if 'v1.1' in versions or 'v1.0' in versions:
-                return True
-        return False
-
 
 class BaseV1ImageTest(BaseImageTest):
 
@@ -92,7 +81,7 @@
     def setUpClass(cls):
         super(BaseV1ImageTest, cls).setUpClass()
         cls.client = cls.os.image_client
-        if not cls._check_version('v1.0'):
+        if not cls.config.image_feature_enabled.api_v1:
             msg = "Glance API v1 not supported"
             raise cls.skipException(msg)
 
@@ -103,6 +92,6 @@
     def setUpClass(cls):
         super(BaseV2ImageTest, cls).setUpClass()
         cls.client = cls.os.image_client_v2
-        if not cls._check_version('v2.0'):
+        if not cls.config.image_feature_enabled.api_v2:
             msg = "Glance API v2 not supported"
             raise cls.skipException(msg)
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index eb3535f..ee6d656 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -20,6 +20,7 @@
 import random
 
 from tempest.api.image import base
+from tempest.common.utils import data_utils
 from tempest import exceptions
 from tempest.test import attr
 
@@ -42,29 +43,46 @@
                           'test', 'bare', 'wrong')
 
     @attr(type='gate')
-    def test_register_then_upload(self):
-        # Register, then upload an image
-        resp, body = self.create_image(name='New Name',
+    def test_register_upload_get_image_file(self):
+
+        """
+        Here we test these functionalities - Register image,
+        upload the image file, get image and get image file api's
+        """
+
+        image_name = data_utils.rand_name('image')
+        resp, body = self.create_image(name=image_name,
                                        container_format='bare',
                                        disk_format='raw',
                                        visibility='public')
         self.assertIn('id', body)
         image_id = body.get('id')
         self.assertIn('name', body)
-        self.assertEqual('New Name', body.get('name'))
+        self.assertEqual(image_name, body['name'])
         self.assertIn('visibility', body)
-        self.assertTrue(body.get('visibility') == 'public')
+        self.assertEqual('public', body['visibility'])
         self.assertIn('status', body)
-        self.assertEqual('queued', body.get('status'))
+        self.assertEqual('queued', body['status'])
 
         # Now try uploading an image file
-        image_file = StringIO.StringIO(('*' * 1024))
+        file_content = '*' * 1024
+        image_file = StringIO.StringIO(file_content)
         resp, body = self.client.store_image(image_id, image_file)
         self.assertEqual(resp.status, 204)
-        resp, body = self.client.get_image_metadata(image_id)
+
+        # Now try to get image details
+        resp, body = self.client.get_image(image_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(image_id, body['id'])
+        self.assertEqual(image_name, body['name'])
         self.assertIn('size', body)
         self.assertEqual(1024, body.get('size'))
 
+        # Now try get image file
+        resp, body = self.client.get_image_file(image_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(file_content, body)
+
 
 class ListImagesTest(base.BaseV2ImageTest):
 
@@ -107,6 +125,6 @@
             self.assertIn(image, image_list)
 
     @attr(type=['negative', 'gate'])
-    def test_get_image_meta_by_null_id(self):
+    def test_get_image_by_null_id(self):
         self.assertRaises(exceptions.NotFound,
-                          self.client.get_image_metadata, '')
+                          self.client.get_image, '')
diff --git a/tempest/api/image/v2/test_images_tags.py b/tempest/api/image/v2/test_images_tags.py
new file mode 100644
index 0000000..e37e462
--- /dev/null
+++ b/tempest/api/image/v2/test_images_tags.py
@@ -0,0 +1,45 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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.image import base
+from tempest.common.utils import data_utils
+from tempest.test import attr
+
+
+class ImagesTagsTest(base.BaseV2ImageTest):
+
+    @attr(type='gate')
+    def test_update_delete_tags_for_image(self):
+        resp, body = self.create_image(container_format='bare',
+                                       disk_format='raw',
+                                       visibility='public')
+        image_id = body['id']
+        tag = data_utils.rand_name('tag-')
+        self.addCleanup(self.client.delete_image, image_id)
+
+        # Creating image tag and verify it.
+        resp, body = self.client.add_image_tag(image_id, tag)
+        self.assertEqual(resp.status, 204)
+        resp, body = self.client.get_image(image_id)
+        self.assertEqual(resp.status, 200)
+        self.assertIn(tag, body['tags'])
+
+        # Deleting image tag and verify it.
+        resp = self.client.delete_image_tag(image_id, tag)
+        self.assertEqual(resp.status, 204)
+        resp, body = self.client.get_image(image_id)
+        self.assertEqual(resp.status, 200)
+        self.assertNotIn(tag, body['tags'])
diff --git a/tempest/api/image/v2/test_images_tags_negative.py b/tempest/api/image/v2/test_images_tags_negative.py
new file mode 100644
index 0000000..e0d84de
--- /dev/null
+++ b/tempest/api/image/v2/test_images_tags_negative.py
@@ -0,0 +1,46 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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 uuid
+
+from tempest.api.image import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class ImagesTagsNegativeTest(base.BaseV2ImageTest):
+
+    @attr(type=['negative', 'gate'])
+    def test_update_tags_for_non_existing_image(self):
+        # Update tag with non existing image.
+        tag = data_utils.rand_name('tag-')
+        non_exist_image = str(uuid.uuid4())
+        self.assertRaises(exceptions.NotFound, self.client.add_image_tag,
+                          non_exist_image, tag)
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_non_existing_tag(self):
+        # Delete non existing tag.
+        resp, body = self.create_image(container_format='bare',
+                                       disk_format='raw',
+                                       is_public=True,
+                                       )
+        image_id = body['id']
+        tag = data_utils.rand_name('non-exist-tag-')
+        self.addCleanup(self.client.delete_image, image_id)
+        self.assertRaises(exceptions.NotFound, self.client.delete_image_tag,
+                          image_id, tag)
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index b6c2679..159c4f5 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -68,75 +68,39 @@
 
     @classmethod
     def tearDownClass(cls):
-        has_exception = False
+        # Clean up vpn services
         for vpnservice in cls.vpnservices:
-            try:
-                cls.client.delete_vpn_service(vpnservice['id'])
-            except Exception as exc:
-                LOG.exception(exc)
-                has_exception = True
-
+            cls.client.delete_vpn_service(vpnservice['id'])
+        # Clean up routers
         for router in cls.routers:
-            try:
-                resp, body = cls.client.list_router_interfaces(router['id'])
-                interfaces = body['ports']
-                for i in interfaces:
-                    cls.client.remove_router_interface_with_subnet_id(
-                        router['id'], i['fixed_ips'][0]['subnet_id'])
-            except Exception as exc:
-                LOG.exception(exc)
-                has_exception = True
-            try:
-                cls.client.delete_router(router['id'])
-            except Exception as exc:
-                LOG.exception(exc)
-                has_exception = True
-
+            resp, body = cls.client.list_router_interfaces(router['id'])
+            interfaces = body['ports']
+            for i in interfaces:
+                cls.client.remove_router_interface_with_subnet_id(
+                    router['id'], i['fixed_ips'][0]['subnet_id'])
+            cls.client.delete_router(router['id'])
+        # Clean up health monitors
         for health_monitor in cls.health_monitors:
-            try:
-                cls.client.delete_health_monitor(health_monitor['id'])
-            except Exception as exc:
-                LOG.exception(exc)
-                has_exception = True
+            cls.client.delete_health_monitor(health_monitor['id'])
+        # Clean up members
         for member in cls.members:
-            try:
-                cls.client.delete_member(member['id'])
-            except Exception as exc:
-                LOG.exception(exc)
-                has_exception = True
+            cls.client.delete_member(member['id'])
+        # Clean up vips
         for vip in cls.vips:
-            try:
-                cls.client.delete_vip(vip['id'])
-            except Exception as exc:
-                LOG.exception(exc)
-                has_exception = True
+            cls.client.delete_vip(vip['id'])
+        # Clean up pools
         for pool in cls.pools:
-            try:
-                cls.client.delete_pool(pool['id'])
-            except Exception as exc:
-                LOG.exception(exc)
-                has_exception = True
+            cls.client.delete_pool(pool['id'])
+        # Clean up ports
         for port in cls.ports:
-            try:
-                cls.client.delete_port(port['id'])
-            except Exception as exc:
-                LOG.exception(exc)
-                has_exception = True
+            cls.client.delete_port(port['id'])
+        # Clean up subnets
         for subnet in cls.subnets:
-            try:
-                cls.client.delete_subnet(subnet['id'])
-            except Exception as exc:
-                LOG.exception(exc)
-                has_exception = True
+            cls.client.delete_subnet(subnet['id'])
+        # Clean up networks
         for network in cls.networks:
-            try:
-                cls.client.delete_network(network['id'])
-            except Exception as exc:
-                LOG.exception(exc)
-                has_exception = True
+            cls.client.delete_network(network['id'])
         super(BaseNetworkTest, cls).tearDownClass()
-        if has_exception:
-            raise exceptions.TearDownException()
 
     @classmethod
     def create_network(cls, network_name=None):
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 65fe1ac..a90d3f4 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -20,14 +20,14 @@
 from tempest import clients
 from tempest.common.utils.data_utils import arbitrary_string
 from tempest.common.utils.data_utils import rand_name
-import tempest.config
+from tempest import config
 from tempest import exceptions
 from tempest.test import attr
 
 
 class AccountQuotasTest(base.BaseObjectTest):
     accounts_quotas_available = \
-        tempest.config.TempestConfig().object_storage.accounts_quotas_available
+        config.TempestConfig().object_storage_feature_enabled.accounts_quotas
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 1a3f775..90b0914 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -101,7 +101,7 @@
                       'limit': limit}
             resp, container_list = \
                 self.account_client.list_account_containers(params=params)
-            self.assertLessEqual(len(container_list), limit)
+            self.assertTrue(len(container_list) <= limit, str(container_list))
 
     @attr(type='smoke')
     def test_list_account_metadata(self):
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index 31fe711..2e0d76a 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -33,7 +33,7 @@
 class ContainerQuotasTest(base.BaseObjectTest):
     """Attemps to test the perfect behavior of quotas in a container."""
     container_quotas_available = \
-        config.TempestConfig().object_storage.container_quotas_available
+        config.TempestConfig().object_storage_feature_enabled.container_quotas
 
     def setUp(self):
         """Creates and sets a container with quotas.
diff --git a/tempest/api/object_storage/test_healthcheck.py b/tempest/api/object_storage/test_healthcheck.py
new file mode 100644
index 0000000..798ea4f
--- /dev/null
+++ b/tempest/api/object_storage/test_healthcheck.py
@@ -0,0 +1,65 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+#
+# Author: Joe H. Rahme <joe.hakim.rahme@enovance.com>
+#
+# 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.object_storage import base
+from tempest import clients
+from tempest.test import attr
+from tempest.test import HTTP_SUCCESS
+
+
+class HealthcheckTest(base.BaseObjectTest):
+
+    @classmethod
+    def setUpClass(cls):
+        super(HealthcheckTest, cls).setUpClass()
+
+        # creates a test user. The test user will set its base_url to the Swift
+        # endpoint and test the healthcheck feature.
+        cls.data.setup_test_user()
+
+        cls.os_test_user = clients.Manager(
+            cls.data.test_user,
+            cls.data.test_password,
+            cls.data.test_tenant)
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.data.teardown_all()
+        super(HealthcheckTest, cls).tearDownClass()
+
+    def setUp(self):
+        super(HealthcheckTest, self).setUp()
+        client = self.os_test_user.account_client
+        client._set_auth()
+
+        # Turning http://.../v1/foobar into http://.../
+        client.base_url = "/".join(client.base_url.split("/")[:-2])
+
+    def tearDown(self):
+        # clear the base_url for subsequent requests
+        self.os_test_user.account_client.base_url = None
+        super(HealthcheckTest, self).tearDown()
+
+    @attr('gate')
+    def test_get_healthcheck(self):
+
+        resp, _ = self.os_test_user.account_client.get("healthcheck", {})
+
+        # The status is expected to be 200
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 797aa71..eada639 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -28,7 +28,7 @@
     @classmethod
     def setUpClass(cls):
         super(VolumeMultiBackendTest, cls).setUpClass()
-        if not cls.config.volume.multi_backend_enabled:
+        if not cls.config.volume_feature_enabled.multi_backend:
             raise cls.skipException("Cinder multi-backend feature disabled")
 
         cls.backend1_name = cls.config.volume.backend1_name
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 98694c5..cdf8638 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -18,7 +18,6 @@
 import time
 
 from tempest import clients
-from tempest.common import isolated_creds
 from tempest.openstack.common import log as logging
 import tempest.test
 
@@ -32,21 +31,12 @@
     @classmethod
     def setUpClass(cls):
         super(BaseVolumeTest, cls).setUpClass()
-        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
 
         if not cls.config.service_available.cinder:
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
 
-        if cls.config.compute.allow_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)
-        else:
-            os = clients.Manager(interface=cls._interface)
+        os = cls.get_client_manager()
 
         cls.os = os
         cls.volumes_client = os.volumes_client
@@ -69,7 +59,7 @@
     def tearDownClass(cls):
         cls.clear_snapshots()
         cls.clear_volumes()
-        cls.isolated_creds.clear_isolated_creds()
+        cls.clear_isolated_creds()
         super(BaseVolumeTest, cls).tearDownClass()
 
     @classmethod
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 09131e2..f12d4bb 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -16,7 +16,7 @@
 #    under the License.
 
 from tempest.api.volume.base import BaseVolumeTest
-from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils import data_utils
 from tempest.test import attr
 from tempest.test import services
 from tempest.test import stresstest
@@ -32,8 +32,8 @@
         cls.image_client = cls.os.image_client
 
         # Create a test shared instance and volume for attach/detach tests
-        srv_name = rand_name(cls.__name__ + '-Instance-')
-        vol_name = rand_name(cls.__name__ + '-Volume-')
+        srv_name = data_utils.rand_name(cls.__name__ + '-Instance-')
+        vol_name = data_utils.rand_name(cls.__name__ + '-Volume-')
         resp, cls.server = cls.servers_client.create_server(srv_name,
                                                             cls.image_ref,
                                                             cls.flavor_ref)
@@ -102,7 +102,7 @@
         # it is shared with the other tests. After it is uploaded in Glance,
         # there is no way to delete it from Cinder, so we delete it from Glance
         # using the Glance image_client and from Cinder via tearDownClass.
-        image_name = rand_name('Image-')
+        image_name = data_utils.rand_name('Image-')
         resp, body = self.client.upload_volume(self.volume['id'],
                                                image_name,
                                                self.config.volume.disk_format)
@@ -112,6 +112,17 @@
         self.image_client.wait_for_image_status(image_id, 'active')
         self.client.wait_for_volume_status(self.volume['id'], 'available')
 
+    @attr(type='gate')
+    def test_volume_extend(self):
+        # Extend Volume Test.
+        extend_size = int(self.volume['size']) + 1
+        resp, body = self.client.extend_volume(self.volume['id'], extend_size)
+        self.assertEqual(202, resp.status)
+        self.client.wait_for_volume_status(self.volume['id'], 'available')
+        resp, volume = self.client.get_volume(self.volume['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(int(volume['size']), extend_size)
+
 
 class VolumesActionsTestXML(VolumesActionsTest):
     _interface = "xml"
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 02adc5d..9bab9a0 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -177,6 +177,41 @@
                           self.client.detach_volume,
                           'xxx')
 
+    @attr(type=['negative', 'gate'])
+    def test_volume_extend_with_size_smaller_than_original_size(self):
+        # Extend volume with smaller size than original size.
+        extend_size = 0
+        self.assertRaises(exceptions.BadRequest, self.client.extend_volume,
+                          self.volume['id'], extend_size)
+
+    @attr(type=['negative', 'gate'])
+    def test_volume_extend_with_non_number_size(self):
+        # Extend volume when size is non number.
+        extend_size = 'abc'
+        self.assertRaises(exceptions.BadRequest, self.client.extend_volume,
+                          self.volume['id'], extend_size)
+
+    @attr(type=['negative', 'gate'])
+    def test_volume_extend_with_None_size(self):
+        # Extend volume with None size.
+        extend_size = None
+        self.assertRaises(exceptions.BadRequest, self.client.extend_volume,
+                          self.volume['id'], extend_size)
+
+    @attr(type=['negative', 'gate'])
+    def test_volume_extend_with_nonexistent_volume_id(self):
+        # Extend volume size when volume is nonexistent.
+        extend_size = int(self.volume['size']) + 1
+        self.assertRaises(exceptions.NotFound, self.client.extend_volume,
+                          str(uuid.uuid4()), extend_size)
+
+    @attr(type=['negative', 'gate'])
+    def test_volume_extend_without_passing_volume_id(self):
+        # Extend volume size when passing volume id is None.
+        extend_size = int(self.volume['size']) + 1
+        self.assertRaises(exceptions.NotFound, self.client.extend_volume,
+                          None, extend_size)
+
 
 class VolumesNegativeTestXML(VolumesNegativeTest):
     _interface = 'xml'
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
new file mode 100644
index 0000000..04a4774
--- /dev/null
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -0,0 +1,44 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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 uuid
+
+from tempest.api.volume import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class VolumesSnapshotNegativeTest(base.BaseVolumeTest):
+    _interface = "json"
+
+    @attr(type=['negative', 'gate'])
+    def test_create_snapshot_with_nonexistent_volume_id(self):
+        # Create a snapshot with nonexistent volume id
+        s_name = data_utils.rand_name('snap')
+        self.assertRaises(exceptions.NotFound,
+                          self.snapshots_client.create_snapshot,
+                          str(uuid.uuid4()), display_name=s_name)
+
+    @attr(type=['negative', 'gate'])
+    def test_create_snapshot_without_passing_volume_id(self):
+        # Create a snapshot without passing volume id
+        s_name = data_utils.rand_name('snap')
+        self.assertRaises(exceptions.NotFound,
+                          self.snapshots_client.create_snapshot,
+                          None, display_name=s_name)
+
+
+class VolumesSnapshotNegativeTestXML(VolumesSnapshotNegativeTest):
+    _interface = "xml"
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index b082b1e..a4c8316 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -80,6 +80,12 @@
         return self.cmd_with_auth(
             'glance', action, flags, params, admin, fail_ok)
 
+    def ceilometer(self, action, flags='', params='', admin=True,
+                   fail_ok=False):
+        """Executes ceilometer command for the given action."""
+        return self.cmd_with_auth(
+            'ceilometer', action, flags, params, admin, fail_ok)
+
     def cinder(self, action, flags='', params='', admin=True, fail_ok=False):
         """Executes cinder command for the given action."""
         return self.cmd_with_auth(
diff --git a/tempest/cli/simple_read_only/test_ceilometer.py b/tempest/cli/simple_read_only/test_ceilometer.py
new file mode 100644
index 0000000..7f2864f
--- /dev/null
+++ b/tempest/cli/simple_read_only/test_ceilometer.py
@@ -0,0 +1,51 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo.config import cfg
+
+import tempest.cli
+from tempest.openstack.common import log as logging
+
+CONF = cfg.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class SimpleReadOnlyCeilometerClientTest(tempest.cli.ClientTestBase):
+    """Basic, read-only tests for Ceilometer CLI client.
+
+    Checks return values and output of read-only commands.
+    These tests do not presume any content, nor do they create
+    their own. They only verify the structure of output if present.
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        if (not CONF.service_available.ceilometer):
+            msg = ("Skiping all Ceilometer cli tests because it is"
+                   "not available")
+            raise cls.skipException(msg)
+        super(SimpleReadOnlyCeilometerClientTest, cls).setUpClass()
+
+    def test_ceilometer_meter_list(self):
+        self.ceilometer('meter-list')
+
+    def test_ceilometer_resource_list(self):
+        self.ceilometer('resource-list')
+
+    def test_ceilometermeter_alarm_list(self):
+        self.ceilometer('alarm-list')
diff --git a/tempest/cli/simple_read_only/test_glance.py b/tempest/cli/simple_read_only/test_glance.py
index d02c60b..a5a229c 100644
--- a/tempest/cli/simple_read_only/test_glance.py
+++ b/tempest/cli/simple_read_only/test_glance.py
@@ -18,9 +18,13 @@
 import re
 import subprocess
 
+from oslo.config import cfg
+
 import tempest.cli
 from tempest.openstack.common import log as logging
 
+CONF = cfg.CONF
+
 
 LOG = logging.getLogger(__name__)
 
@@ -45,6 +49,14 @@
             'ID', 'Name', 'Disk Format', 'Container Format',
             'Size', 'Status'])
 
+    def test_glance_member_list(self):
+        tenant_name = '--tenant-id %s' % self.identity.admin_tenant_name
+        out = self.glance('member-list',
+                          params=tenant_name)
+        endpoints = self.parser.listing(out)
+        self.assertTableStruct(endpoints,
+                               ['Image ID', 'Member ID', 'Can Share'])
+
     def test_glance_help(self):
         help_text = self.glance('help')
         lines = help_text.split('\n')
@@ -64,3 +76,14 @@
                                'member-add', 'member-create', 'member-delete',
                                'member-list'))
         self.assertFalse(wanted_commands - commands)
+
+    # Optional arguments:
+
+    def test_glance_version(self):
+        self.glance('', flags='--version')
+
+    def test_glance_debug_list(self):
+        self.glance('image-list', flags='--debug')
+
+    def test_glance_timeout(self):
+        self.glance('image-list', flags='--timeout %d' % CONF.cli.timeout)
diff --git a/tempest/config.py b/tempest/config.py
index db923e9..effa5a1 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -137,28 +137,6 @@
                default="password",
                help="Password used to authenticate to an instance using "
                     "the alternate image."),
-    cfg.BoolOpt('resize_available',
-                default=False,
-                help="Does the test environment support resizing?"),
-    cfg.BoolOpt('live_migration_available',
-                default=False,
-                help="Does the test environment support live migration "
-                     "available?"),
-    cfg.BoolOpt('use_block_migration_for_live_migration',
-                default=False,
-                help="Does the test environment use block devices for live "
-                     "migration"),
-    cfg.BoolOpt('block_migrate_supports_cinder_iscsi',
-                default=False,
-                help="Does the test environment block migration support "
-                     "cinder iSCSI volumes"),
-    cfg.BoolOpt('change_password_available',
-                default=False,
-                help="Does the test environment support changing the admin "
-                     "password?"),
-    cfg.BoolOpt('create_image_enabled',
-                default=False,
-                help="Does the test environment support snapshots?"),
     cfg.IntOpt('build_interval',
                default=10,
                help="Time in seconds between build status checks."),
@@ -212,18 +190,47 @@
                default=None,
                help="Path to a private key file for SSH access to remote "
                     "hosts"),
-    cfg.BoolOpt('disk_config_enabled',
-                default=True,
-                help="If false, skip disk config tests"),
-    cfg.BoolOpt('flavor_extra_enabled',
-                default=True,
-                help="If false, skip flavor extra data test"),
     cfg.StrOpt('volume_device_name',
                default='vdb',
                help="Expected device name when a volume is attached to "
                     "an instance")
 ]
 
+compute_features_group = cfg.OptGroup(name='compute-feature-enabled',
+                                      title="Enabled Compute Service Features")
+
+ComputeFeaturesGroup = [
+    cfg.BoolOpt('disk_config',
+                default=True,
+                help="If false, skip disk config tests"),
+    cfg.BoolOpt('flavor_extra',
+                default=True,
+                help="If false, skip flavor extra data test"),
+    cfg.BoolOpt('change_password',
+                default=False,
+                help="Does the test environment support changing the admin "
+                     "password?"),
+    cfg.BoolOpt('create_image',
+                default=False,
+                help="Does the test environment support snapshots?"),
+    cfg.BoolOpt('resize',
+                default=False,
+                help="Does the test environment support resizing?"),
+    cfg.BoolOpt('live_migration',
+                default=False,
+                help="Does the test environment support live migration "
+                     "available?"),
+    cfg.BoolOpt('block_migration_for_live_migration',
+                default=False,
+                help="Does the test environment use block devices for live "
+                     "migration"),
+    cfg.BoolOpt('block_migrate_cinder_iscsi',
+                default=False,
+                help="Does the test environment block migration support "
+                     "cinder iSCSI volumes")
+]
+
+
 compute_admin_group = cfg.OptGroup(name='compute-admin',
                                    title="Compute Admin Options")
 
@@ -245,9 +252,6 @@
                            title="Image Service Options")
 
 ImageGroup = [
-    cfg.StrOpt('api_version',
-               default='1',
-               help="Version of the API"),
     cfg.StrOpt('catalog_type',
                default='image',
                help='Catalog type of the Image service.'),
@@ -263,6 +267,17 @@
                help='http accessible image')
 ]
 
+image_feature_group = cfg.OptGroup(name='image-feature-enabled',
+                                   title='Enabled image service features')
+
+ImageFeaturesGroup = [
+    cfg.BoolOpt('api_v2',
+                default=True,
+                help="Is the v2 image API enabled"),
+    cfg.BoolOpt('api_v1',
+                default=True,
+                help="Is the v1 image API enabled"),
+]
 
 network_group = cfg.OptGroup(name='network',
                              title='Network Service Options')
@@ -317,9 +332,6 @@
                     "of identity.region is used instead. If no such region "
                     "is found in the service catalog, the first found one is "
                     "used."),
-    cfg.BoolOpt('multi_backend_enabled',
-                default=False,
-                help="Runs Cinder multi-backend test (requires 2 backends)"),
     cfg.StrOpt('backend1_name',
                default='BACKEND_1',
                help="Name of the backend1 (must be declared in cinder.conf)"),
@@ -337,6 +349,15 @@
                help='Disk format to use when copying a volume to image'),
 ]
 
+volume_feature_group = cfg.OptGroup(name='volume-feature-enabled',
+                                    title='Enabled Cinder Features')
+
+VolumeFeaturesGroup = [
+    cfg.BoolOpt('multi_backend',
+                default=False,
+                help="Runs Cinder multi-backend test (requires 2 backends)")
+]
+
 
 object_storage_group = cfg.OptGroup(name='object-storage',
                                     title='Object Storage Service Options')
@@ -359,19 +380,26 @@
                default=5,
                help="Number of seconds to wait while looping to check the"
                     "status of a container to container synchronization"),
-    cfg.BoolOpt('accounts_quotas_available',
-                default=True,
-                help="Set to True if the Account Quota middleware is enabled"),
-    cfg.BoolOpt('container_quotas_available',
-                default=True,
-                help="Set to True if the container quota middleware "
-                     "is enabled"),
     cfg.StrOpt('operator_role',
                default='Member',
                help="Role to add to users created for swift tests to "
                     "enable creating containers"),
 ]
 
+object_storage_feature_group = cfg.OptGroup(
+    name='object-storage-feature-enabled',
+    title='Enabled object-storage features')
+
+ObjectStoreFeaturesGroup = [
+    cfg.BoolOpt('container_quotas',
+                default=True,
+                help="Set to True if the container quota middleware "
+                     "is enabled"),
+    cfg.BoolOpt('accounts_quotas',
+                default=True,
+                help="Set to True if the Account Quota middleware is enabled"),
+]
+
 
 orchestration_group = cfg.OptGroup(name='orchestration',
                                    title='Orchestration Service Options')
@@ -410,7 +438,7 @@
                default=None,
                help="Name of existing keypair to launch servers with."),
     cfg.IntOpt('max_template_size',
-               default=32768,
+               default=524288,
                help="Value must match heat configuration of the same name."),
 ]
 
@@ -557,6 +585,9 @@
     cfg.BoolOpt('heat',
                 default=False,
                 help="Whether or not Heat is expected to be available"),
+    cfg.BoolOpt('ceilometer',
+                default=True,
+                help="Whether or not Ceilometer is expected to be available"),
     cfg.BoolOpt('horizon',
                 default=True,
                 help="Whether or not Horizon is expected to be available"),
@@ -611,11 +642,18 @@
         LOG.info("Using tempest config file %s" % path)
 
         register_opt_group(cfg.CONF, compute_group, ComputeGroup)
+        register_opt_group(cfg.CONF, compute_features_group,
+                           ComputeFeaturesGroup)
         register_opt_group(cfg.CONF, identity_group, IdentityGroup)
         register_opt_group(cfg.CONF, image_group, ImageGroup)
+        register_opt_group(cfg.CONF, image_feature_group, ImageFeaturesGroup)
         register_opt_group(cfg.CONF, network_group, NetworkGroup)
         register_opt_group(cfg.CONF, volume_group, VolumeGroup)
+        register_opt_group(cfg.CONF, volume_feature_group,
+                           VolumeFeaturesGroup)
         register_opt_group(cfg.CONF, object_storage_group, ObjectStoreGroup)
+        register_opt_group(cfg.CONF, object_storage_feature_group,
+                           ObjectStoreFeaturesGroup)
         register_opt_group(cfg.CONF, orchestration_group, OrchestrationGroup)
         register_opt_group(cfg.CONF, dashboard_group, DashboardGroup)
         register_opt_group(cfg.CONF, boto_group, BotoGroup)
@@ -626,11 +664,16 @@
                            ServiceAvailableGroup)
         register_opt_group(cfg.CONF, debug_group, DebugGroup)
         self.compute = cfg.CONF.compute
+        self.compute_feature_enabled = cfg.CONF['compute-feature-enabled']
         self.identity = cfg.CONF.identity
         self.images = cfg.CONF.image
+        self.image_feature_enabled = cfg.CONF['image-feature-enabled']
         self.network = cfg.CONF.network
         self.volume = cfg.CONF.volume
+        self.volume_feature_enabled = cfg.CONF['volume-feature-enabled']
         self.object_storage = cfg.CONF['object-storage']
+        self.object_storage_feature_enabled = cfg.CONF[
+            'object-storage-feature-enabled']
         self.orchestration = cfg.CONF.orchestration
         self.dashboard = cfg.CONF.dashboard
         self.boto = cfg.CONF.boto
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 8d05fd8..67406b0 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -81,6 +81,10 @@
     message = "Server %(server_id)s failed to build and is in ERROR status"
 
 
+class ImageKilledException(TempestException):
+    message = "Image %(image_id)s 'killed' while waiting for %(status)s"
+
+
 class AddImageException(TempestException):
     message = "Image %(image_id)s failed to become ACTIVE in the allotted time"
 
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 853b1ba..112c8a2 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -35,7 +35,7 @@
     def setUpClass(cls):
         super(TestServerAdvancedOps, cls).setUpClass()
 
-        if not cls.config.compute.resize_available:
+        if not cls.config.compute_feature_enabled.resize:
             msg = "Skipping test - resize not available on this host"
             raise cls.skipException(msg)
 
diff --git a/tempest/services/compute/json/aggregates_client.py b/tempest/services/compute/json/aggregates_client.py
index 75ce9ff..b7c6bf1 100644
--- a/tempest/services/compute/json/aggregates_client.py
+++ b/tempest/services/compute/json/aggregates_client.py
@@ -97,3 +97,14 @@
                                post_body, self.headers)
         body = json.loads(body)
         return resp, body['aggregate']
+
+    def set_metadata(self, aggregate_id, meta):
+        """Replaces the aggregate's existing metadata with new metadata."""
+        post_body = {
+            'metadata': meta,
+        }
+        post_body = json.dumps({'set_metadata': post_body})
+        resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
+                               post_body, self.headers)
+        body = json.loads(body)
+        return resp, body['aggregate']
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index 30a3f7b..f51879d 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -44,3 +44,39 @@
         resp, body = self.get("os-hosts/%s" % str(hostname))
         body = json.loads(body)
         return resp, body['host']
+
+    def update_host(self, hostname, **kwargs):
+        """Update a host."""
+
+        request_body = {
+            'status': None,
+            'maintenance_mode': None,
+        }
+        request_body.update(**kwargs)
+        request_body = json.dumps(request_body)
+
+        resp, body = self.put("os-hosts/%s" % str(hostname), request_body,
+                              self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def startup_host(self, hostname):
+        """Startup a host."""
+
+        resp, body = self.get("os-hosts/%s/startup" % str(hostname))
+        body = json.loads(body)
+        return resp, body['host']
+
+    def shutdown_host(self, hostname):
+        """Shutdown a host."""
+
+        resp, body = self.get("os-hosts/%s/shutdown" % str(hostname))
+        body = json.loads(body)
+        return resp, body['host']
+
+    def reboot_host(self, hostname):
+        """reboot a host."""
+
+        resp, body = self.get("os-hosts/%s/reboot" % str(hostname))
+        body = json.loads(body)
+        return resp, body['host']
diff --git a/tempest/services/compute/xml/aggregates_client.py b/tempest/services/compute/xml/aggregates_client.py
index 8ef0af6..5faaff5 100644
--- a/tempest/services/compute/xml/aggregates_client.py
+++ b/tempest/services/compute/xml/aggregates_client.py
@@ -21,6 +21,7 @@
 from tempest import exceptions
 from tempest.services.compute.xml.common import Document
 from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import Text
 from tempest.services.compute.xml.common import xml_to_json
 
 
@@ -112,3 +113,18 @@
                                self.headers)
         aggregate = self._format_aggregate(etree.fromstring(body))
         return resp, aggregate
+
+    def set_metadata(self, aggregate_id, meta):
+        """Replaces the aggregate's existing metadata with new metadata."""
+        post_body = Element("set_metadata")
+        metadata = Element("metadata")
+        post_body.append(metadata)
+        for k, v in meta.items():
+            meta = Element(k)
+            meta.append(Text(v))
+            metadata.append(meta)
+        resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
+                               str(Document(post_body)),
+                               self.headers)
+        aggregate = self._format_aggregate(etree.fromstring(body))
+        return resp, aggregate
diff --git a/tempest/services/compute/xml/hosts_client.py b/tempest/services/compute/xml/hosts_client.py
index 9743143..f7d7b0a 100644
--- a/tempest/services/compute/xml/hosts_client.py
+++ b/tempest/services/compute/xml/hosts_client.py
@@ -18,6 +18,8 @@
 
 from lxml import etree
 from tempest.common.rest_client import RestClientXML
+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
 
 
@@ -47,3 +49,46 @@
         node = etree.fromstring(body)
         body = [xml_to_json(x) for x in node.getchildren()]
         return resp, body
+
+    def update_host(self, hostname, status=None, maintenance_mode=None,
+                    **kwargs):
+        """Update a host."""
+
+        request_body = Element(status=status,
+                               maintenance_mode=maintenance_mode)
+        if kwargs:
+            for k, v in kwargs.iteritem():
+                request_body.add_attr(k, v)
+        resp, body = self.put("os-hosts/%s" % str(hostname),
+                              str(Document(request_body)),
+                              self.headers)
+        node = etree.fromstring(body)
+        body = [xml_to_json(x) for x in node.getchildren()]
+        return resp, body
+
+    def startup_host(self, hostname):
+        """Startup a host."""
+
+        resp, body = self.get("os-hosts/%s/startup" % str(hostname),
+                              self.headers)
+        node = etree.fromstring(body)
+        body = [xml_to_json(x) for x in node.getchildren()]
+        return resp, body
+
+    def shutdown_host(self, hostname):
+        """Shutdown a host."""
+
+        resp, body = self.get("os-hosts/%s/shutdown" % str(hostname),
+                              self.headers)
+        node = etree.fromstring(body)
+        body = [xml_to_json(x) for x in node.getchildren()]
+        return resp, body
+
+    def reboot_host(self, hostname):
+        """Reboot a host."""
+
+        resp, body = self.get("os-hosts/%s/reboot" % str(hostname),
+                              self.headers)
+        node = etree.fromstring(body)
+        body = [xml_to_json(x) for x in node.getchildren()]
+        return resp, body
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 1921d78..41b9e81 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -265,9 +265,12 @@
                 LOG.info('Value transition from "%s" to "%s"'
                          'in %d second(s).', old_value,
                          value, dtime)
-            if (value == status):
+            if value == status:
                 return value
 
+            if value == 'killed':
+                raise exceptions.ImageKilledException(image_id=image_id,
+                                                      status=value)
             if dtime > self.build_timeout:
                 message = ('Time Limit Exceeded! (%ds)'
                            'while waiting for %s, '
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index f0531ec..342a09c 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -100,7 +100,7 @@
         self._validate_schema(body, type='images')
         return resp, body['images']
 
-    def get_image_metadata(self, image_id):
+    def get_image(self, image_id):
         url = 'v2/images/%s' % image_id
         resp, body = self.get(url)
         body = json.loads(body)
@@ -108,7 +108,7 @@
 
     def is_resource_deleted(self, id):
         try:
-            self.get_image_metadata(id)
+            self.get_image(id)
         except exceptions.NotFound:
             return True
         return False
@@ -124,3 +124,13 @@
         url = 'v2/images/%s/file' % image_id
         resp, body = self.get(url)
         return resp, body
+
+    def add_image_tag(self, image_id, tag):
+        url = 'v2/images/%s/tags/%s' % (image_id, tag)
+        resp, body = self.put(url, body=None, headers=self.headers)
+        return resp, body
+
+    def delete_image_tag(self, image_id, tag):
+        url = 'v2/images/%s/tags/%s' % (image_id, tag)
+        resp, _ = self.delete(url)
+        return resp
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index 62a6e24..f054a2b 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -154,3 +154,13 @@
         except exceptions.NotFound:
             return True
         return False
+
+    def extend_volume(self, volume_id, extend_size):
+        """Extend a volume."""
+        post_body = {
+            'new_size': extend_size
+        }
+        post_body = json.dumps({'os-extend': post_body})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body, self.headers)
+        return resp, body
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index b59ec03..f5d8beb 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -227,3 +227,13 @@
         resp, body = self.post(url, str(Document(post_body)), self.headers)
         volume = xml_to_json(etree.fromstring(body))
         return resp, volume
+
+    def extend_volume(self, volume_id, extend_size):
+        """Extend a volume."""
+        post_body = Element("os-extend",
+                            new_size=extend_size)
+        url = 'volumes/%s/action' % str(volume_id)
+        resp, body = self.post(url, str(Document(post_body)), self.headers)
+        if body:
+            body = xml_to_json(etree.fromstring(body))
+        return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index 8ce7af8..edba709 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -26,6 +26,7 @@
 import testtools
 
 from tempest import clients
+from tempest.common import isolated_creds
 from tempest import config
 from tempest import exceptions
 from tempest.openstack.common import log as logging
@@ -216,7 +217,36 @@
             os.environ.get('OS_LOG_CAPTURE') != '0'):
             log_format = '%(asctime)-15s %(message)s'
             self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
-                                                   format=log_format))
+                                                   format=log_format,
+                                                   level=None))
+
+    @classmethod
+    def get_client_manager(cls):
+        """
+        Returns an Openstack client manager
+        """
+        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
+
+        force_tenant_isolation = getattr(cls, 'force_tenant_isolation', None)
+        if (cls.config.compute.allow_tenant_isolation or
+            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)
+        else:
+            os = clients.Manager(interface=cls._interface)
+        return os
+
+    @classmethod
+    def clear_isolated_creds(cls):
+        """
+        Clears isolated creds if set
+        """
+        if getattr(cls, 'isolated_creds'):
+            cls.isolated_creds.clear_isolated_creds()
 
     @classmethod
     def _get_identity_admin_client(cls):
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index ffaf134..0ae3323 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -46,14 +46,24 @@
     test methods that have been decorated to skip because of
     a particular bug.
     """
-    results = []
+    results = {}
     debug("Searching in %s", start)
     for root, _dirs, files in os.walk(start):
         for name in files:
             if name.startswith('test_') and name.endswith('py'):
                 path = os.path.join(root, name)
                 debug("Searching in %s", path)
-                results += find_skips_in_file(path)
+                temp_result = find_skips_in_file(path)
+                for method_name, bug_no in temp_result:
+                    if results.get(bug_no):
+                        result_dict = results.get(bug_no)
+                        if result_dict.get(name):
+                            result_dict[name].append(method_name)
+                        else:
+                            result_dict[name] = [method_name]
+                        results[bug_no] = result_dict
+                    else:
+                        results[bug_no] = {name: [method_name]}
     return results
 
 
@@ -83,11 +93,19 @@
     return results
 
 
+def get_results(result_dict):
+    results = []
+    for bug_no in result_dict.keys():
+        for method in result_dict[bug_no]:
+            results.append((method, bug_no))
+    return results
+
+
 if __name__ == '__main__':
     logging.basicConfig(format='%(levelname)s: %(message)s',
                         level=logging.INFO)
     results = find_skips()
-    unique_bugs = sorted(set([bug for (method, bug) in results]))
+    unique_bugs = sorted(set([bug for (method, bug) in get_results(results)]))
     unskips = []
     duplicates = []
     info("Total bug skips found: %d", len(results))
@@ -122,4 +140,7 @@
         print("should be removed from the test cases:")
         print()
         for bug in unskips:
-            print("  %7s" % bug)
+            message = "  %7s in " % bug
+            locations = ["%s" % x for x in results[bug].keys()]
+            message += " and ".join(locations)
+            print(message)