Merge "remove  test_service_enable_disable in nova V3 tests"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 937bbd3..1080ddf 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -43,7 +43,7 @@
 #logging_exception_prefix=%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s
 
 # list of logger=LEVEL pairs (list value)
-#default_log_levels=amqplib=WARN,sqlalchemy=WARN,boto=WARN,suds=INFO,keystone=INFO
+#default_log_levels=amqplib=WARN,sqlalchemy=WARN,boto=WARN,suds=INFO,keystone=INFO,paramiko=INFO
 
 # publish error events (boolean value)
 #publish_errors=false
@@ -524,6 +524,16 @@
 #disk_format=raw
 
 
+[debug]
+
+#
+# Options defined in tempest.config
+#
+
+# Enable diagnostic commands (boolean value)
+#enable=true
+
+
 [dashboard]
 
 #
@@ -610,14 +620,15 @@
 #operator_role=Member
 
 
-[debug]
+[network-feature-enabled]
 
 #
 # Options defined in tempest.config
 #
 
-# Enable diagnostic commands (boolean value)
-#enable=true
+# A list of enabled extensions with a special entry all which
+# indicates every extension is enabled (list value)
+#api_extensions=all
 
 
 [service_available]
@@ -671,8 +682,13 @@
 # If false, skip disk config tests (boolean value)
 #disk_config=true
 
-# If false, skip flavor extra data test (boolean value)
-#flavor_extra=true
+# A list of enabled extensions with a special entry all which
+# indicates every extension is enabled (list value)
+#api_extensions=all
+
+# A list of enabled v3 extensions with a special entry all
+# which indicates every extension is enabled (list value)
+#api_v3_extensions=all
 
 # Does the test environment support changing the admin
 # password? (boolean value)
@@ -730,4 +746,8 @@
 # (boolean value)
 #multi_backend=false
 
+# A list of enabled extensions with a special entry all which
+# indicates every extension is enabled (list value)
+#api_extensions=all
+
 
diff --git a/etc/whitelist.yaml b/etc/whitelist.yaml
index e5a0d4d..a822fae 100644
--- a/etc/whitelist.yaml
+++ b/etc/whitelist.yaml
@@ -160,14 +160,6 @@
     - module: "cinder.brick.local_dev.lvm"
       message: "Can't remove open logical volume"
 
-q-dhpc:
-    - module: "neutron.common.legacy"
-      message: "Skipping unknown group key: firewall_driver"
-    - module: "neutron.agent.dhcp_agent"
-      message: "Unable to enable dhcp"
-    - module: "neutron.agent.dhcp_agent"
-      message: " Network .* RPC info call failed"
-
 ceilometer-collector:
     - module: "stevedore.extension"
       message: ".*"
diff --git a/requirements.txt b/requirements.txt
index 4f6a1d3..cd11aa7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,9 +5,9 @@
 jsonschema>=1.3.0,!=1.4.0
 testtools>=0.9.32
 lxml>=2.3
-boto>=2.4.0,!=2.13.0
+boto>=2.12.0,!=2.13.0
 paramiko>=1.8.0
-netaddr
+netaddr>=0.7.6
 python-glanceclient>=0.9.0
 python-keystoneclient>=0.4.1
 python-novaclient>=2.15.0
@@ -20,5 +20,5 @@
 oslo.config>=1.2.0
 eventlet>=0.13.0
 six>=1.4.1
-iso8601>=0.1.4
+iso8601>=0.1.8
 fixtures>=0.3.14
diff --git a/run_tests.sh b/run_tests.sh
index 5c8ce7d..3c9c051 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -11,7 +11,6 @@
   echo "  -u, --update             Update the virtual environment with any newer package versions"
   echo "  -s, --smoke              Only run smoke tests"
   echo "  -t, --serial             Run testr serially"
-  echo "  -c, --nova-coverage      Enable Nova coverage collection"
   echo "  -C, --config             Config file location"
   echo "  -p, --pep8               Just run pep8"
   echo "  -h, --help               Print this usage message"
@@ -31,13 +30,12 @@
 no_site_packages=0
 force=0
 wrapper=""
-nova_coverage=0
 config_file=""
 update=0
 logging=0
 logging_config=etc/logging.conf
 
-if ! options=$(getopt -o VNnfustcphdC:lL: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,serial,nova-coverage,pep8,help,debug,config:,logging,logging-config: -- "$@")
+if ! options=$(getopt -o VNnfustphdC:lL: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,serial,pep8,help,debug,config:,logging,logging-config: -- "$@")
 then
     # parse error
     usage
@@ -55,7 +53,6 @@
     -f|--force) force=1;;
     -u|--update) update=1;;
     -d|--debug) set -o xtrace;;
-    -c|--nova-coverage) let nova_coverage=1;;
     -C|--config) config_file=$2; shift;;
     -p|--pep8) let just_pep8=1;;
     -s|--smoke) testrargs+="smoke"; noseargs+="--attr=type=smoke";;
@@ -131,16 +128,6 @@
   ${wrapper} flake8
 }
 
-function run_coverage_start {
-  echo "Starting nova-coverage"
-  ${wrapper} python tools/tempest_coverage.py -c start
-}
-
-function run_coverage_report {
-  echo "Generating nova-coverage report"
-  ${wrapper} python tools/tempest_coverage.py -c report
-}
-
 if [ $never_venv -eq 0 ]
 then
   # Remove the virtual environment if --force used
@@ -176,11 +163,6 @@
     exit
 fi
 
-if [ $nova_coverage -eq 1 ]; then
-    run_coverage_start
-fi
-
-
 py_version=`${wrapper} python --version 2>&1`
 if [[ $py_version =~ "2.6" ]] ; then
     run_tests_nose
@@ -189,10 +171,6 @@
 fi
 retval=$?
 
-if [ $nova_coverage -eq 1 ]; then
-    run_coverage_report
-fi
-
 if [ -z "$testrargs" ]; then
     run_pep8
 fi
diff --git a/tempest/api/compute/__init__.py b/tempest/api/compute/__init__.py
index a528754..dd92ee9 100644
--- a/tempest/api/compute/__init__.py
+++ b/tempest/api/compute/__init__.py
@@ -26,7 +26,6 @@
 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_flavors.py b/tempest/api/compute/admin/test_flavors.py
index c5a8772..cf72e49 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -17,12 +17,10 @@
 
 import uuid
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest import exceptions
-from tempest.test import attr
-from tempest.test import skip_because
+from tempest import test
 
 
 class FlavorsAdminTestJSON(base.BaseV2ComputeAdminTest):
@@ -36,7 +34,7 @@
     @classmethod
     def setUpClass(cls):
         super(FlavorsAdminTestJSON, cls).setUpClass()
-        if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+        if not test.is_extension_enabled('FlavorExtraData', 'compute'):
             msg = "FlavorExtraData extension not enabled."
             raise cls.skipException(msg)
 
@@ -87,19 +85,19 @@
 
         return flavor['id']
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_flavor_with_int_id(self):
         flavor_id = data_utils.rand_int_id(start=1000)
         new_flavor_id = self._create_flavor(flavor_id)
         self.assertEqual(new_flavor_id, str(flavor_id))
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_flavor_with_uuid_id(self):
         flavor_id = str(uuid.uuid4())
         new_flavor_id = self._create_flavor(flavor_id)
         self.assertEqual(new_flavor_id, flavor_id)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_flavor_with_none_id(self):
         # If nova receives a request with None as flavor_id,
         # nova generates flavor_id of uuid.
@@ -107,7 +105,7 @@
         new_flavor_id = self._create_flavor(flavor_id)
         self.assertEqual(new_flavor_id, str(uuid.UUID(new_flavor_id)))
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_flavor_verify_entry_in_list_details(self):
         # Create a flavor and ensure it's details are listed
         # This operation requires the user to have 'admin' role
@@ -132,7 +130,7 @@
                 flag = True
         self.assertTrue(flag)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_get_flavor_details_for_deleted_flavor(self):
         # Delete a flavor and ensure it is not listed
         # Create a test flavor
@@ -166,7 +164,7 @@
                 flag = False
         self.assertTrue(flag)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_list_flavor_without_extra_data(self):
         # Create a flavor and ensure it is listed
         # This operation requires the user to have 'admin' role
@@ -210,8 +208,8 @@
                 flag = True
         self.assertTrue(flag)
 
-    @skip_because(bug="1209101")
-    @attr(type='gate')
+    @test.skip_because(bug="1209101")
+    @test.attr(type='gate')
     def test_list_non_public_flavor(self):
         # Create a flavor with os-flavor-access:is_public false should
         # be present in list_details.
@@ -244,7 +242,7 @@
                 flag = True
         self.assertFalse(flag)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_server_with_non_public_flavor(self):
         # Create a flavor with os-flavor-access:is_public false
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -264,7 +262,7 @@
                           self.os.servers_client.create_server,
                           'test', self.image_ref, flavor['id'])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_list_public_flavor_with_other_user(self):
         # Create a Flavor with public access.
         # Try to List/Get flavor with another user
@@ -288,7 +286,7 @@
                 flag = True
         self.assertTrue(flag)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_is_public_string_variations(self):
         flavor_id_not_public = data_utils.rand_int_id(start=1000)
         flavor_name_not_public = data_utils.rand_name(self.flavor_name_prefix)
@@ -331,7 +329,7 @@
         _test_string_variations(['t', 'true', 'yes', '1'],
                                 flavor_name_public)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_flavor_using_string_ram(self):
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
         new_flavor_id = data_utils.rand_int_id(start=1000)
@@ -349,13 +347,13 @@
         self.assertEqual(flavor['ram'], int(ram))
         self.assertEqual(int(flavor['id']), new_flavor_id)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_invalid_is_public_string(self):
         self.assertRaises(exceptions.BadRequest,
                           self.client.list_flavors_with_detail,
                           {'is_public': 'invalid'})
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_create_flavor_as_user(self):
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
         new_flavor_id = data_utils.rand_int_id(start=1000)
@@ -366,13 +364,13 @@
                           new_flavor_id, ephemeral=self.ephemeral,
                           swap=self.swap, rxtx=self.rxtx)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_delete_flavor_as_user(self):
         self.assertRaises(exceptions.Unauthorized,
                           self.user_client.delete_flavor,
                           self.flavor_ref_alt)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_create_flavor_using_invalid_ram(self):
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
         new_flavor_id = data_utils.rand_int_id(start=1000)
@@ -382,7 +380,7 @@
                           flavor_name, -1, self.vcpus,
                           self.disk, new_flavor_id)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_create_flavor_using_invalid_vcpus(self):
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
         new_flavor_id = data_utils.rand_int_id(start=1000)
diff --git a/tempest/api/compute/admin/test_flavors_access.py b/tempest/api/compute/admin/test_flavors_access.py
index b866db1..048312b 100644
--- a/tempest/api/compute/admin/test_flavors_access.py
+++ b/tempest/api/compute/admin/test_flavors_access.py
@@ -15,10 +15,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
 
 
 class FlavorsAccessTestJSON(base.BaseV2ComputeAdminTest):
@@ -33,7 +32,7 @@
     @classmethod
     def setUpClass(cls):
         super(FlavorsAccessTestJSON, cls).setUpClass()
-        if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+        if not test.is_extension_enabled('FlavorExtraData', 'compute'):
             msg = "FlavorExtraData extension not enabled."
             raise cls.skipException(msg)
 
@@ -51,7 +50,7 @@
         cls.vcpus = 1
         cls.disk = 10
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_flavor_access_list_with_private_flavor(self):
         # Test to list flavor access successfully by querying private flavor
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -70,7 +69,7 @@
         self.assertEqual(str(new_flavor_id), str(first_flavor['flavor_id']))
         self.assertEqual(self.adm_tenant_id, first_flavor['tenant_id'])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_flavor_access_add_remove(self):
         # Test to add and remove flavor access to a given tenant.
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 340c1c7..976124e 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -17,11 +17,10 @@
 
 import uuid
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest import exceptions
-from tempest.test import attr
+from tempest import test
 
 
 class FlavorsAccessNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -36,7 +35,7 @@
     @classmethod
     def setUpClass(cls):
         super(FlavorsAccessNegativeTestJSON, cls).setUpClass()
-        if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+        if not test.is_extension_enabled('FlavorExtraData', 'compute'):
             msg = "FlavorExtraData extension not enabled."
             raise cls.skipException(msg)
 
@@ -54,7 +53,7 @@
         cls.vcpus = 1
         cls.disk = 10
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_access_list_with_public_flavor(self):
         # Test to list flavor access with exceptions by querying public flavor
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -70,7 +69,7 @@
                           self.client.list_flavor_access,
                           new_flavor_id)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_add(self):
         # Test to add flavor access as a user without admin privileges.
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -86,7 +85,7 @@
                           new_flavor['id'],
                           self.tenant_id)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_remove(self):
         # Test to remove flavor access as a user without admin privileges.
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -106,7 +105,7 @@
                           new_flavor['id'],
                           self.tenant_id)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_add_flavor_access_duplicate(self):
         # Create a new flavor.
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -130,7 +129,7 @@
                           new_flavor['id'],
                           self.tenant_id)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_remove_flavor_access_not_found(self):
         # Create a new flavor.
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs.py b/tempest/api/compute/admin/test_flavors_extra_specs.py
index 49b0429..875f742 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs.py
@@ -15,10 +15,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
 
 
 class FlavorsExtraSpecsTestJSON(base.BaseV2ComputeAdminTest):
@@ -34,7 +33,7 @@
     @classmethod
     def setUpClass(cls):
         super(FlavorsExtraSpecsTestJSON, cls).setUpClass()
-        if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+        if not test.is_extension_enabled('FlavorExtraData', 'compute'):
             msg = "FlavorExtraData extension not enabled."
             raise cls.skipException(msg)
 
@@ -61,7 +60,7 @@
         cls.client.wait_for_resource_deletion(cls.flavor['id'])
         super(FlavorsExtraSpecsTestJSON, cls).tearDownClass()
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_flavor_set_get_update_show_unset_keys(self):
         # Test to SET, GET, UPDATE, SHOW, UNSET flavor extra
         # spec as a user with admin privileges.
@@ -101,7 +100,7 @@
             self.client.unset_flavor_extra_spec(self.flavor['id'], "key2")
         self.assertEqual(unset_resp.status, 200)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_flavor_non_admin_get_all_keys(self):
         specs = {"key1": "value1", "key2": "value2"}
         set_resp, set_body = self.client.set_flavor_extra_spec(
@@ -113,7 +112,7 @@
         for key in specs:
             self.assertEqual(body[key], specs[key])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_flavor_non_admin_get_specific_key(self):
         specs = {"key1": "value1", "key2": "value2"}
         resp, body = self.client.set_flavor_extra_spec(
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index d7e1f9f..fb09a63 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -16,11 +16,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest import exceptions
-from tempest.test import attr
+from tempest import test
 
 
 class FlavorsExtraSpecsNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -35,7 +34,7 @@
     @classmethod
     def setUpClass(cls):
         super(FlavorsExtraSpecsNegativeTestJSON, cls).setUpClass()
-        if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+        if not test.is_extension_enabled('FlavorExtraData', 'compute'):
             msg = "FlavorExtraData extension not enabled."
             raise cls.skipException(msg)
 
@@ -62,7 +61,7 @@
         cls.client.wait_for_resource_deletion(cls.flavor['id'])
         super(FlavorsExtraSpecsNegativeTestJSON, cls).tearDownClass()
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_set_keys(self):
         # Test to SET flavor extra spec as a user without admin privileges.
         specs = {"key1": "value1", "key2": "value2"}
@@ -71,7 +70,7 @@
                           self.flavor['id'],
                           specs)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_update_specific_key(self):
         # non admin user is not allowed to update flavor extra spec
         specs = {"key1": "value1", "key2": "value2"}
@@ -86,7 +85,7 @@
                           'key1',
                           key1='value1_new')
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_unset_keys(self):
         specs = {"key1": "value1", "key2": "value2"}
         set_resp, set_body = self.client.set_flavor_extra_spec(
@@ -97,7 +96,7 @@
                           self.flavor['id'],
                           'key1')
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_unset_nonexistent_key(self):
         nonexistent_key = data_utils.rand_name('flavor_key')
         self.assertRaises(exceptions.NotFound,
@@ -105,14 +104,14 @@
                           self.flavor['id'],
                           nonexistent_key)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_get_nonexistent_key(self):
         self.assertRaises(exceptions.NotFound,
                           self.flavors_client.get_flavor_extra_spec_with_key,
                           self.flavor['id'],
                           "nonexistent_key")
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_update_mismatch_key(self):
         # the key will be updated should be match the key in the body
         self.assertRaises(exceptions.BadRequest,
@@ -121,7 +120,7 @@
                           "key2",
                           key1="value")
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_update_more_key(self):
         # there should be just one item in the request body
         self.assertRaises(exceptions.BadRequest,
diff --git a/tempest/api/compute/admin/test_services.py b/tempest/api/compute/admin/test_services.py
index 2b5f0d9..6122758 100644
--- a/tempest/api/compute/admin/test_services.py
+++ b/tempest/api/compute/admin/test_services.py
@@ -17,7 +17,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest import exceptions
 from tempest.test import attr
 
 
@@ -41,11 +40,6 @@
         self.assertEqual(200, resp.status)
         self.assertNotEqual(0, len(services))
 
-    @attr(type=['negative', 'gate'])
-    def test_list_services_with_non_admin_user(self):
-        self.assertRaises(exceptions.Unauthorized,
-                          self.non_admin_client.list_services)
-
     @attr(type='gate')
     def test_get_service_by_service_binary_name(self):
         binary_name = 'nova-compute'
@@ -74,15 +68,6 @@
         # on order.
         self.assertEqual(sorted(s1), sorted(s2))
 
-    @attr(type=['negative', 'gate'])
-    def test_get_service_by_invalid_params(self):
-        # return all services if send the request with invalid parameter
-        resp, services = self.client.list_services()
-        params = {'xxx': 'nova-compute'}
-        resp, services_xxx = self.client.list_services(params)
-        self.assertEqual(200, resp.status)
-        self.assertEqual(len(services), len(services_xxx))
-
     @attr(type='gate')
     def test_get_service_by_service_and_host_name(self):
         resp, services = self.client.list_services()
@@ -95,24 +80,6 @@
         self.assertEqual(host_name, services[0]['host'])
         self.assertEqual(binary_name, services[0]['binary'])
 
-    @attr(type=['negative', 'gate'])
-    def test_get_service_by_invalid_service_and_valid_host(self):
-        resp, services = self.client.list_services()
-        host_name = services[0]['host']
-        params = {'host': host_name, 'binary': 'xxx'}
-        resp, services = self.client.list_services(params)
-        self.assertEqual(200, resp.status)
-        self.assertEqual(0, len(services))
-
-    @attr(type=['negative', 'gate'])
-    def test_get_service_with_valid_service_and_invalid_host(self):
-        resp, services = self.client.list_services()
-        binary_name = services[0]['binary']
-        params = {'host': 'xxx', 'binary': binary_name}
-        resp, services = self.client.list_services(params)
-        self.assertEqual(200, resp.status)
-        self.assertEqual(0, len(services))
-
 
 class ServicesAdminTestXML(ServicesAdminTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
new file mode 100644
index 0000000..da1482e
--- /dev/null
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -0,0 +1,70 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.compute import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class ServicesAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+
+    """
+    Tests Services API. List and Enable/Disable require admin privileges.
+    """
+
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(ServicesAdminNegativeTestJSON, cls).setUpClass()
+        cls.client = cls.os_adm.services_client
+        cls.non_admin_client = cls.services_client
+
+    @attr(type=['negative', 'gate'])
+    def test_list_services_with_non_admin_user(self):
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.list_services)
+
+    @attr(type=['negative', 'gate'])
+    def test_get_service_by_invalid_params(self):
+        # return all services if send the request with invalid parameter
+        resp, services = self.client.list_services()
+        params = {'xxx': 'nova-compute'}
+        resp, services_xxx = self.client.list_services(params)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(len(services), len(services_xxx))
+
+    @attr(type=['negative', 'gate'])
+    def test_get_service_by_invalid_service_and_valid_host(self):
+        resp, services = self.client.list_services()
+        host_name = services[0]['host']
+        params = {'host': host_name, 'binary': 'xxx'}
+        resp, services = self.client.list_services(params)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(0, len(services))
+
+    @attr(type=['negative', 'gate'])
+    def test_get_service_with_valid_service_and_invalid_host(self):
+        resp, services = self.client.list_services()
+        binary_name = services[0]['binary']
+        params = {'host': 'xxx', 'binary': binary_name}
+        resp, services = self.client.list_services(params)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(0, len(services))
+
+
+class ServicesAdminNegativeTestXML(ServicesAdminNegativeTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 1ba9b16..3c00851 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -178,6 +178,11 @@
                                                     kwargs['wait_until'])
             resp, image = cls.images_client.get_image(image_id)
 
+            if kwargs['wait_until'] == 'ACTIVE':
+                if kwargs.get('wait_for_server', True):
+                    cls.servers_client.wait_for_server_status(server_id,
+                                                              'ACTIVE')
+
         return resp, image
 
     @classmethod
@@ -239,6 +244,7 @@
         cls.interfaces_client = cls.os.interfaces_v3_client
         cls.hypervisor_client = cls.os.hypervisor_v3_client
         cls.tenant_usages_client = cls.os.tenant_usages_v3_client
+        cls.volumes_client = cls.os.volumes_client
 
     @classmethod
     def create_image_from_server(cls, server_id, **kwargs):
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index 4539981..55260bf 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -98,7 +98,8 @@
         snapshot_name = data_utils.rand_name('test-snap-')
         resp, image = self.create_image_from_server(server['id'],
                                                     name=snapshot_name,
-                                                    wait_until='ACTIVE')
+                                                    wait_until='ACTIVE',
+                                                    wait_for_server=False)
         self.addCleanup(self.client.delete_image, image['id'])
         self.assertEqual(snapshot_name, image['name'])
 
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index 1401654..ac2ecba 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -16,7 +16,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest import exceptions
 from tempest.openstack.common import log as logging
 from tempest.test import attr
@@ -45,24 +44,21 @@
                                                       'ACTIVE')
 
             # Create images to be used in the filter tests
-            resp, body = cls.create_image_from_server(cls.server1['id'])
-            cls.image1_id = data_utils.parse_image_id(resp['location'])
-            cls.client.wait_for_image_status(cls.image1_id, 'ACTIVE')
-            resp, cls.image1 = cls.client.get_image(cls.image1_id)
+            resp, cls.image1 = cls.create_image_from_server(
+                cls.server1['id'], wait_until='ACTIVE')
+            cls.image1_id = cls.image1['id']
 
             # Servers have a hidden property for when they are being imaged
             # Performing back-to-back create image calls on a single
             # server will sometimes cause failures
-            resp, body = cls.create_image_from_server(cls.server2['id'])
-            cls.image3_id = data_utils.parse_image_id(resp['location'])
-            cls.client.wait_for_image_status(cls.image3_id, 'ACTIVE')
-            resp, cls.image3 = cls.client.get_image(cls.image3_id)
+            resp, cls.image3 = cls.create_image_from_server(
+                cls.server2['id'], wait_until='ACTIVE')
+            cls.image3_id = cls.image3['id']
 
-            resp, body = cls.create_image_from_server(cls.server1['id'])
-            cls.image2_id = data_utils.parse_image_id(resp['location'])
-
-            cls.client.wait_for_image_status(cls.image2_id, 'ACTIVE')
-            resp, cls.image2 = cls.client.get_image(cls.image2_id)
+            # Wait for the server to be active after the image upload
+            resp, cls.image2 = cls.create_image_from_server(
+                cls.server1['id'], wait_until='ACTIVE')
+            cls.image2_id = cls.image2['id']
         except Exception as exc:
             LOG.exception(exc)
             cls.tearDownClass()
diff --git a/tempest/api/compute/v3/admin/test_flavors_access.py b/tempest/api/compute/v3/admin/test_flavors_access.py
index b866db1..048312b 100644
--- a/tempest/api/compute/v3/admin/test_flavors_access.py
+++ b/tempest/api/compute/v3/admin/test_flavors_access.py
@@ -15,10 +15,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
 
 
 class FlavorsAccessTestJSON(base.BaseV2ComputeAdminTest):
@@ -33,7 +32,7 @@
     @classmethod
     def setUpClass(cls):
         super(FlavorsAccessTestJSON, cls).setUpClass()
-        if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+        if not test.is_extension_enabled('FlavorExtraData', 'compute'):
             msg = "FlavorExtraData extension not enabled."
             raise cls.skipException(msg)
 
@@ -51,7 +50,7 @@
         cls.vcpus = 1
         cls.disk = 10
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_flavor_access_list_with_private_flavor(self):
         # Test to list flavor access successfully by querying private flavor
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -70,7 +69,7 @@
         self.assertEqual(str(new_flavor_id), str(first_flavor['flavor_id']))
         self.assertEqual(self.adm_tenant_id, first_flavor['tenant_id'])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_flavor_access_add_remove(self):
         # Test to add and remove flavor access to a given tenant.
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
diff --git a/tempest/api/compute/v3/admin/test_flavors_access_negative.py b/tempest/api/compute/v3/admin/test_flavors_access_negative.py
index 340c1c7..976124e 100644
--- a/tempest/api/compute/v3/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/v3/admin/test_flavors_access_negative.py
@@ -17,11 +17,10 @@
 
 import uuid
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest import exceptions
-from tempest.test import attr
+from tempest import test
 
 
 class FlavorsAccessNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -36,7 +35,7 @@
     @classmethod
     def setUpClass(cls):
         super(FlavorsAccessNegativeTestJSON, cls).setUpClass()
-        if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+        if not test.is_extension_enabled('FlavorExtraData', 'compute'):
             msg = "FlavorExtraData extension not enabled."
             raise cls.skipException(msg)
 
@@ -54,7 +53,7 @@
         cls.vcpus = 1
         cls.disk = 10
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_access_list_with_public_flavor(self):
         # Test to list flavor access with exceptions by querying public flavor
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -70,7 +69,7 @@
                           self.client.list_flavor_access,
                           new_flavor_id)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_add(self):
         # Test to add flavor access as a user without admin privileges.
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -86,7 +85,7 @@
                           new_flavor['id'],
                           self.tenant_id)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_remove(self):
         # Test to remove flavor access as a user without admin privileges.
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -106,7 +105,7 @@
                           new_flavor['id'],
                           self.tenant_id)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_add_flavor_access_duplicate(self):
         # Create a new flavor.
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
@@ -130,7 +129,7 @@
                           new_flavor['id'],
                           self.tenant_id)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_remove_flavor_access_not_found(self):
         # Create a new flavor.
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
diff --git a/tempest/api/compute/v3/admin/test_flavors_extra_specs.py b/tempest/api/compute/v3/admin/test_flavors_extra_specs.py
index 49b0429..875f742 100644
--- a/tempest/api/compute/v3/admin/test_flavors_extra_specs.py
+++ b/tempest/api/compute/v3/admin/test_flavors_extra_specs.py
@@ -15,10 +15,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
 
 
 class FlavorsExtraSpecsTestJSON(base.BaseV2ComputeAdminTest):
@@ -34,7 +33,7 @@
     @classmethod
     def setUpClass(cls):
         super(FlavorsExtraSpecsTestJSON, cls).setUpClass()
-        if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+        if not test.is_extension_enabled('FlavorExtraData', 'compute'):
             msg = "FlavorExtraData extension not enabled."
             raise cls.skipException(msg)
 
@@ -61,7 +60,7 @@
         cls.client.wait_for_resource_deletion(cls.flavor['id'])
         super(FlavorsExtraSpecsTestJSON, cls).tearDownClass()
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_flavor_set_get_update_show_unset_keys(self):
         # Test to SET, GET, UPDATE, SHOW, UNSET flavor extra
         # spec as a user with admin privileges.
@@ -101,7 +100,7 @@
             self.client.unset_flavor_extra_spec(self.flavor['id'], "key2")
         self.assertEqual(unset_resp.status, 200)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_flavor_non_admin_get_all_keys(self):
         specs = {"key1": "value1", "key2": "value2"}
         set_resp, set_body = self.client.set_flavor_extra_spec(
@@ -113,7 +112,7 @@
         for key in specs:
             self.assertEqual(body[key], specs[key])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_flavor_non_admin_get_specific_key(self):
         specs = {"key1": "value1", "key2": "value2"}
         resp, body = self.client.set_flavor_extra_spec(
diff --git a/tempest/api/compute/v3/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/v3/admin/test_flavors_extra_specs_negative.py
index d7e1f9f..fb09a63 100644
--- a/tempest/api/compute/v3/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/v3/admin/test_flavors_extra_specs_negative.py
@@ -16,11 +16,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest import exceptions
-from tempest.test import attr
+from tempest import test
 
 
 class FlavorsExtraSpecsNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -35,7 +34,7 @@
     @classmethod
     def setUpClass(cls):
         super(FlavorsExtraSpecsNegativeTestJSON, cls).setUpClass()
-        if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+        if not test.is_extension_enabled('FlavorExtraData', 'compute'):
             msg = "FlavorExtraData extension not enabled."
             raise cls.skipException(msg)
 
@@ -62,7 +61,7 @@
         cls.client.wait_for_resource_deletion(cls.flavor['id'])
         super(FlavorsExtraSpecsNegativeTestJSON, cls).tearDownClass()
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_set_keys(self):
         # Test to SET flavor extra spec as a user without admin privileges.
         specs = {"key1": "value1", "key2": "value2"}
@@ -71,7 +70,7 @@
                           self.flavor['id'],
                           specs)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_update_specific_key(self):
         # non admin user is not allowed to update flavor extra spec
         specs = {"key1": "value1", "key2": "value2"}
@@ -86,7 +85,7 @@
                           'key1',
                           key1='value1_new')
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_unset_keys(self):
         specs = {"key1": "value1", "key2": "value2"}
         set_resp, set_body = self.client.set_flavor_extra_spec(
@@ -97,7 +96,7 @@
                           self.flavor['id'],
                           'key1')
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_unset_nonexistent_key(self):
         nonexistent_key = data_utils.rand_name('flavor_key')
         self.assertRaises(exceptions.NotFound,
@@ -105,14 +104,14 @@
                           self.flavor['id'],
                           nonexistent_key)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_get_nonexistent_key(self):
         self.assertRaises(exceptions.NotFound,
                           self.flavors_client.get_flavor_extra_spec_with_key,
                           self.flavor['id'],
                           "nonexistent_key")
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_update_mismatch_key(self):
         # the key will be updated should be match the key in the body
         self.assertRaises(exceptions.BadRequest,
@@ -121,7 +120,7 @@
                           "key2",
                           key1="value")
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_flavor_update_more_key(self):
         # there should be just one item in the request body
         self.assertRaises(exceptions.BadRequest,
diff --git a/tempest/api/compute/v3/servers/test_server_rescue.py b/tempest/api/compute/v3/servers/test_server_rescue.py
index 1008670..eebd4d8 100644
--- a/tempest/api/compute/v3/servers/test_server_rescue.py
+++ b/tempest/api/compute/v3/servers/test_server_rescue.py
@@ -16,87 +16,69 @@
 #    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 ServerRescueTestJSON(base.BaseV2ComputeTest):
+class ServerRescueV3TestJSON(base.BaseV3ComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(ServerRescueTestJSON, cls).setUpClass()
+        super(ServerRescueV3TestJSON, cls).setUpClass()
         cls.device = 'vdf'
 
-        # Floating IP creation
-        resp, body = cls.floating_ips_client.create_floating_ip()
-        cls.floating_ip_id = str(body['id']).strip()
-        cls.floating_ip = str(body['ip']).strip()
-
-        # Security group creation
-        cls.sg_name = data_utils.rand_name('sg')
-        cls.sg_desc = data_utils.rand_name('sg-desc')
-        resp, cls.sg = \
-            cls.security_groups_client.create_security_group(cls.sg_name,
-                                                             cls.sg_desc)
-        cls.sg_id = cls.sg['id']
-
         # Create a volume and wait for it to become ready for attach
         resp, cls.volume_to_attach = \
-            cls.volumes_extensions_client.create_volume(1,
-                                                        display_name=
-                                                        'test_attach')
-        cls.volumes_extensions_client.wait_for_volume_status(
+            cls.volumes_client.create_volume(1,
+                                             display_name=
+                                             'test_attach')
+        cls.volumes_client.wait_for_volume_status(
             cls.volume_to_attach['id'], 'available')
 
         # Create a volume and wait for it to become ready for attach
         resp, cls.volume_to_detach = \
-            cls.volumes_extensions_client.create_volume(1,
-                                                        display_name=
-                                                        'test_detach')
-        cls.volumes_extensions_client.wait_for_volume_status(
+            cls.volumes_client.create_volume(1,
+                                             display_name=
+                                             'test_detach')
+        cls.volumes_client.wait_for_volume_status(
             cls.volume_to_detach['id'], 'available')
 
         # Server for positive tests
         resp, server = cls.create_test_server(wait_until='BUILD')
         resp, resc_server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
-        cls.password = server['adminPass']
+        cls.password = server['admin_password']
         cls.servers_client.wait_for_server_status(cls.server_id, 'ACTIVE')
 
         # Server for negative tests
         cls.rescue_id = resc_server['id']
-        cls.rescue_password = resc_server['adminPass']
+        cls.rescue_password = resc_server['admin_password']
 
         cls.servers_client.rescue_server(
             cls.rescue_id, cls.rescue_password)
         cls.servers_client.wait_for_server_status(cls.rescue_id, 'RESCUE')
 
     def setUp(self):
-        super(ServerRescueTestJSON, self).setUp()
+        super(ServerRescueV3TestJSON, self).setUp()
 
     @classmethod
     def tearDownClass(cls):
-        # Deleting the floating IP which is created in this method
-        cls.floating_ips_client.delete_floating_ip(cls.floating_ip_id)
-        client = cls.volumes_extensions_client
+        client = cls.volumes_client
         client.delete_volume(str(cls.volume_to_attach['id']).strip())
         client.delete_volume(str(cls.volume_to_detach['id']).strip())
-        resp, cls.sg = cls.security_groups_client.delete_security_group(
-            cls.sg_id)
-        super(ServerRescueTestJSON, cls).tearDownClass()
+        super(ServerRescueV3TestJSON, cls).tearDownClass()
 
     def tearDown(self):
-        super(ServerRescueTestJSON, self).tearDown()
+        super(ServerRescueV3TestJSON, self).tearDown()
 
     def _detach(self, server_id, volume_id):
         self.servers_client.detach_volume(server_id, volume_id)
-        self.volumes_extensions_client.wait_for_volume_status(volume_id,
-                                                              'available')
+        self.volumes_client.wait_for_volume_status(volume_id,
+                                                   'available')
 
     def _delete(self, volume_id):
-        self.volumes_extensions_client.delete_volume(volume_id)
+        self.volumes_client.delete_volume(volume_id)
 
     def _unrescue(self, server_id):
         resp, body = self.servers_client.unrescue_server(server_id)
@@ -112,7 +94,7 @@
     def test_rescue_unrescue_instance(self):
         resp, body = self.servers_client.rescue_server(
             self.server_id, self.password)
-        self.assertEqual(200, resp.status)
+        self.assertEqual(202, resp.status)
         self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
         resp, body = self.servers_client.unrescue_server(self.server_id)
         self.assertEqual(202, resp.status)
@@ -169,7 +151,7 @@
         self.servers_client.attach_volume(self.server_id,
                                           self.volume_to_detach['id'],
                                           device='/dev/%s' % self.device)
-        self.volumes_extensions_client.wait_for_volume_status(
+        self.volumes_client.wait_for_volume_status(
             self.volume_to_detach['id'], 'in-use')
 
         # Rescue the server
@@ -186,48 +168,6 @@
                           self.server_id,
                           self.volume_to_detach['id'])
 
-    @attr(type='gate')
-    def test_rescued_vm_associate_dissociate_floating_ip(self):
-        # Rescue the server
-        self.servers_client.rescue_server(
-            self.server_id, self.password)
-        self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
-        self.addCleanup(self._unrescue, self.server_id)
 
-        # Association of floating IP to a rescued vm
-        client = self.floating_ips_client
-        resp, body = client.associate_floating_ip_to_server(self.floating_ip,
-                                                            self.server_id)
-        self.assertEqual(202, resp.status)
-
-        # Disassociation of floating IP that was associated in this method
-        resp, body = \
-            client.disassociate_floating_ip_from_server(self.floating_ip,
-                                                        self.server_id)
-        self.assertEqual(202, resp.status)
-
-    @attr(type='gate')
-    def test_rescued_vm_add_remove_security_group(self):
-        # Rescue the server
-        self.servers_client.rescue_server(
-            self.server_id, self.password)
-        self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
-
-        # Add Security group
-        resp, body = self.servers_client.add_security_group(self.server_id,
-                                                            self.sg_name)
-        self.assertEqual(202, resp.status)
-
-        # Delete Security group
-        resp, body = self.servers_client.remove_security_group(self.server_id,
-                                                               self.sg_name)
-        self.assertEqual(202, resp.status)
-
-        # Unrescue the server
-        resp, body = self.servers_client.unrescue_server(self.server_id)
-        self.assertEqual(202, resp.status)
-        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
-
-
-class ServerRescueTestXML(ServerRescueTestJSON):
+class ServerRescueV3TestXML(ServerRescueV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/config.py b/tempest/config.py
index 220fd04..3d9eba9 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -208,9 +208,14 @@
     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.ListOpt('api_extensions',
+                default=['all'],
+                help='A list of enabled extensions with a special entry all '
+                     'which indicates every extension is enabled'),
+    cfg.ListOpt('api_v3_extensions',
+                default=['all'],
+                help='A list of enabled v3 extensions with a special entry all'
+                     ' which indicates every extension is enabled'),
     cfg.BoolOpt('change_password',
                 default=False,
                 help="Does the test environment support changing the admin "
@@ -317,6 +322,16 @@
                     "connectivity"),
 ]
 
+network_feature_group = cfg.OptGroup(name='network-feature-enabled',
+                                     title='Enabled network service features')
+
+NetworkFeaturesGroup = [
+    cfg.ListOpt('api_extensions',
+                default=['all'],
+                help='A list of enabled extensions with a special entry all '
+                     'which indicates every extension is enabled'),
+]
+
 volume_group = cfg.OptGroup(name='volume',
                             title='Block Storage Options')
 
@@ -360,7 +375,11 @@
 VolumeFeaturesGroup = [
     cfg.BoolOpt('multi_backend',
                 default=False,
-                help="Runs Cinder multi-backend test (requires 2 backends)")
+                help="Runs Cinder multi-backend test (requires 2 backends)"),
+    cfg.ListOpt('api_extensions',
+                default=['all'],
+                help='A list of enabled extensions with a special entry all '
+                     'which indicates every extension is enabled'),
 ]
 
 
@@ -659,6 +678,8 @@
         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, network_feature_group,
+                           NetworkFeaturesGroup)
         register_opt_group(cfg.CONF, volume_group, VolumeGroup)
         register_opt_group(cfg.CONF, volume_feature_group,
                            VolumeFeaturesGroup)
@@ -680,6 +701,7 @@
         self.images = cfg.CONF.image
         self.image_feature_enabled = cfg.CONF['image-feature-enabled']
         self.network = cfg.CONF.network
+        self.network_feature_enabled = cfg.CONF['network-feature-enabled']
         self.volume = cfg.CONF.volume
         self.volume_feature_enabled = cfg.CONF['volume-feature-enabled']
         self.object_storage = cfg.CONF['object-storage']
diff --git a/tempest/openstack/common/log.py b/tempest/openstack/common/log.py
index 5cf8ed6..abb44ef 100644
--- a/tempest/openstack/common/log.py
+++ b/tempest/openstack/common/log.py
@@ -132,6 +132,7 @@
                     'boto=WARN',
                     'suds=INFO',
                     'keystone=INFO',
+                    'paramiko=INFO'
                 ],
                 help='list of logger=LEVEL pairs'),
     cfg.BoolOpt('publish_errors',
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index cddbb53..a7fcc6d 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -121,7 +121,7 @@
             post_body['access_ip_v6'] = access_ip_v6
 
         if disk_config is not None:
-            post_body['OS-DCF:diskConfig'] = disk_config
+            post_body['os-disk-config:disk_config'] = disk_config
 
         post_body = json.dumps({'server': post_body})
         resp, body = self.put("servers/%s" % str(server_id),
@@ -309,14 +309,6 @@
         """Detaches a volume from a server instance."""
         return self.action(server_id, 'detach', None, volume_id=volume_id)
 
-    def add_security_group(self, server_id, name):
-        """Adds a security group to the server."""
-        return self.action(server_id, 'add_security_group', None, name=name)
-
-    def remove_security_group(self, server_id, name):
-        """Removes a security group from the server."""
-        return self.action(server_id, 'remove_security_group', None, name=name)
-
     def live_migrate_server(self, server_id, dest_host, use_block_migration):
         """This should be called with administrator privileges ."""
 
diff --git a/tempest/services/compute/v3/xml/servers_client.py b/tempest/services/compute/v3/xml/servers_client.py
index 2ad5849..7af4161 100644
--- a/tempest/services/compute/v3/xml/servers_client.py
+++ b/tempest/services/compute/v3/xml/servers_client.py
@@ -254,6 +254,10 @@
             server.add_attr("access_ip_v4", access_ip_v4)
         if access_ip_v6 is not None:
             server.add_attr("access_ip_v6", access_ip_v6)
+        if disk_config is not None:
+            server.add_attr('xmlns:os-disk-config', "http://docs.openstack.org"
+                            "/compute/ext/disk_config/api/v3")
+            server.add_attr("os-disk-config:disk_config", disk_config)
         if meta is not None:
             metadata = Element("metadata")
             server.append(metadata)
@@ -511,12 +515,6 @@
                                str(Document(post_body)), self.headers)
         return resp, body
 
-    def add_security_group(self, server_id, name):
-        return self.action(server_id, 'add_security_group', None, name=name)
-
-    def remove_security_group(self, server_id, name):
-        return self.action(server_id, 'remove_security_group', None, name=name)
-
     def live_migrate_server(self, server_id, dest_host, use_block_migration):
         """This should be called with administrator privileges ."""
 
diff --git a/tempest/test.py b/tempest/test.py
index 6ae7925..ceb2c80 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -123,6 +123,42 @@
     return decorator
 
 
+def requires_ext(*args, **kwargs):
+    """A decorator to skip tests if an extension is not enabled
+
+    @param extension
+    @param service
+    """
+    def decorator(func):
+        @functools.wraps(func)
+        def wrapper(*func_args, **func_kwargs):
+            if not is_extension_enabled(kwargs['extension'],
+                                        kwargs['service']):
+                msg = "Skipped because %s extension: %s is not enabled" % (
+                    kwargs['service'], kwargs['extension'])
+                raise testtools.TestCase.skipException(msg)
+            return func(*func_args, **func_kwargs)
+        return wrapper
+    return decorator
+
+
+def is_extension_enabled(extension_name, service):
+    """A function that will check the list of enabled extensions from config
+
+    """
+    configs = config.TempestConfig()
+    config_dict = {
+        'compute': configs.compute_feature_enabled.api_extensions,
+        'compute_v3': configs.compute_feature_enabled.api_v3_extensions,
+        'volume': configs.volume_feature_enabled.api_extensions,
+        'network': configs.network_feature_enabled.api_extensions,
+    }
+    if config_dict[service][0] == 'all':
+        return True
+    if extension_name in config_dict[service]:
+        return True
+    return False
+
 # there is a mis-match between nose and testtools for older pythons.
 # testtools will set skipException to be either
 # unittest.case.SkipTest, unittest2.case.SkipTest or an internal skip
diff --git a/test-requirements.txt b/test-requirements.txt
index 41a784e..9486244 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,7 +1,8 @@
 hacking>=0.8.0,<0.9
 # needed for doc build
 docutils==0.9.1
-sphinx>=1.1.2
+sphinx>=1.1.2,<1.2
 python-subunit
 oslo.sphinx
 mox>=0.5.3
+mock>=1.0
diff --git a/tools/tempest_coverage.py b/tools/tempest_coverage.py
deleted file mode 100755
index ef2eacd..0000000
--- a/tools/tempest_coverage.py
+++ /dev/null
@@ -1,193 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 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.
-
-import json
-import os
-import shutil
-import sys
-
-from oslo.config import cfg
-
-from tempest.common.rest_client import RestClient
-from tempest import config
-
-CONF = config.TempestConfig()
-
-
-class CoverageClientJSON(RestClient):
-
-    def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(CoverageClientJSON, self).__init__(config, username, password,
-                                                 auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
-
-    def start_coverage(self):
-        post_body = {
-            'start': {},
-        }
-        post_body = json.dumps(post_body)
-        return self.post('os-coverage/action', post_body, self.headers)
-
-    def start_coverage_combine(self):
-        post_body = {
-            'start': {
-                'combine': True,
-            },
-        }
-        post_body = json.dumps(post_body)
-        return self.post('os-coverage/action', post_body, self.headers)
-
-    def stop_coverage(self):
-        post_body = {
-            'stop': {},
-        }
-        post_body = json.dumps(post_body)
-        resp, body = self.post('os-coverage/action', post_body, self.headers)
-        body = json.loads(body)
-        return resp, body
-
-    def report_coverage_xml(self, file=None):
-        post_body = {
-            'report': {
-                'file': 'coverage.report',
-                'xml': True,
-            },
-        }
-        if file:
-            post_body['report']['file'] = file
-        post_body = json.dumps(post_body)
-        resp, body = self.post('os-coverage/action', post_body, self.headers)
-        body = json.loads(body)
-        return resp, body
-
-    def report_coverage(self, file=None):
-        post_body = {
-            'report': {
-                'file': 'coverage.report',
-            },
-        }
-        if file:
-            post_body['report']['file'] = file
-        post_body = json.dumps(post_body)
-        resp, body = self.post('os-coverage/action', post_body, self.headers)
-        body = json.loads(body)
-        return resp, body
-
-    def report_coverage_html(self, file=None):
-        post_body = {
-            'report': {
-                'file': 'coverage.report',
-                'html': True,
-            },
-        }
-        if file:
-            post_body['report']['file'] = file
-        post_body = json.dumps(post_body)
-        resp, body = self.post('os-coverage/action', post_body, self.headers)
-        body = json.loads(body)
-        return resp, body
-
-
-def parse_opts(argv):
-    cli_opts = [
-        cfg.StrOpt('command',
-                   short='c',
-                   default='',
-                   help="This required argument is used to specify the "
-                        "coverage command to run. Only 'start', "
-                        "'stop', or 'report' are valid fields."),
-        cfg.StrOpt('filename',
-                   default='tempest-coverage',
-                   help="Specify a filename to be used for generated report "
-                        "files"),
-        cfg.BoolOpt('xml',
-                    default=False,
-                    help='Generate XML reports instead of text'),
-        cfg.BoolOpt('html',
-                    default=False,
-                    help='Generate HTML reports instead of text'),
-        cfg.BoolOpt('combine',
-                    default=False,
-                    help='Generate a single report for all services'),
-        cfg.StrOpt('output',
-                   short='o',
-                   default=None,
-                   help='Optional directory to copy generated coverage data or'
-                        ' reports into. This directory must not already exist '
-                        'it will be created')
-    ]
-    CLI = cfg.ConfigOpts()
-    CLI.register_cli_opts(cli_opts)
-    CLI(argv[1:])
-    return CLI
-
-
-def main(argv):
-    CLI = parse_opts(argv)
-    client_args = (CONF, CONF.identity.admin_username,
-                   CONF.identity.admin_password, CONF.identity.uri,
-                   CONF.identity.admin_tenant_name)
-    coverage_client = CoverageClientJSON(*client_args)
-
-    if CLI.command == 'start':
-        if CLI.combine:
-            coverage_client.start_coverage_combine()
-        else:
-            coverage_client.start_coverage()
-
-    elif CLI.command == 'stop':
-        resp, body = coverage_client.stop_coverage()
-        if not resp['status'] == '200':
-            print('coverage stop failed with: %s:' % (resp['status'] + ': '
-                                                      + body))
-            exit(int(resp['status']))
-        path = body['path']
-        if CLI.output:
-            shutil.copytree(path, CLI.output)
-        else:
-            print("Data files located at: %s" % path)
-
-    elif CLI.command == 'report':
-        if CLI.xml:
-            resp, body = coverage_client.report_coverage_xml(file=CLI.filename)
-        elif CLI.html:
-            resp, body = coverage_client.report_coverage_html(
-                file=CLI.filename)
-        else:
-            resp, body = coverage_client.report_coverage(file=CLI.filename)
-        if not resp['status'] == '200':
-            print('coverage report failed with: %s:' % (resp['status'] + ': '
-                                                        + body))
-            exit(int(resp['status']))
-        path = body['path']
-        if CLI.output:
-            if CLI.html:
-                shutil.copytree(path, CLI.output)
-            else:
-                path = os.path.dirname(path)
-                shutil.copytree(path, CLI.output)
-        else:
-            if not CLI.html:
-                path = os.path.dirname(path)
-            print('Report files located at: %s' % path)
-
-    else:
-        print('Invalid command')
-        exit(1)
-
-
-if __name__ == "__main__":
-    main(sys.argv)
diff --git a/tox.ini b/tox.ini
index 6efac78..e5698d2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -88,13 +88,6 @@
 commands =
    sh tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])((smoke)|(^tempest\.scenario)) {posargs}'
 
-[testenv:coverage]
-sitepackages = True
-commands =
-   python -m tools/tempest_coverage -c start --combine
-   sh tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli))'
-   python -m tools/tempest_coverage -c report --html {posargs}
-
 [testenv:stress]
 sitepackages = True
 commands =