Merge "api-tests: Common way to define required extensions"
diff --git a/neutron/tests/tempest/api/admin/test_agent_management.py b/neutron/tests/tempest/api/admin/test_agent_management.py
index 08f2bc3..a46721f 100644
--- a/neutron/tests/tempest/api/admin/test_agent_management.py
+++ b/neutron/tests/tempest/api/admin/test_agent_management.py
@@ -14,15 +14,15 @@
 
 from neutron.tests.tempest.common import tempest_fixtures
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base
 
 
 class AgentManagementTestJSON(base.BaseAdminNetworkTest):
 
+    required_extensions = ['agent']
+
     @classmethod
-    @test.requires_ext(extension="agent", service="network")
     def resource_setup(cls):
         super(AgentManagementTestJSON, cls).resource_setup()
         body = cls.admin_client.list_agents()
diff --git a/neutron/tests/tempest/api/admin/test_dhcp_agent_scheduler.py b/neutron/tests/tempest/api/admin/test_dhcp_agent_scheduler.py
index 40febdf..2a24496 100644
--- a/neutron/tests/tempest/api/admin/test_dhcp_agent_scheduler.py
+++ b/neutron/tests/tempest/api/admin/test_dhcp_agent_scheduler.py
@@ -14,7 +14,6 @@
 
 from neutron_lib import constants
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.common import utils
 from neutron.tests.tempest.api import base
@@ -22,8 +21,9 @@
 
 class DHCPAgentSchedulersTestJSON(base.BaseAdminNetworkTest):
 
+    required_extensions = ['dhcp_agent_scheduler']
+
     @classmethod
-    @test.requires_ext(extension="dhcp_agent_scheduler", service="network")
     def resource_setup(cls):
         super(DHCPAgentSchedulersTestJSON, cls).resource_setup()
         # Create a network and make sure it will be hosted by a
diff --git a/neutron/tests/tempest/api/admin/test_extension_driver_port_security_admin.py b/neutron/tests/tempest/api/admin/test_extension_driver_port_security_admin.py
index f1fc3d0..9e12179 100644
--- a/neutron/tests/tempest/api/admin/test_extension_driver_port_security_admin.py
+++ b/neutron/tests/tempest/api/admin/test_extension_driver_port_security_admin.py
@@ -24,9 +24,10 @@
 class PortSecurityAdminTests(base_security.BaseSecGroupTest,
                              base.BaseAdminNetworkTest):
 
+    required_extensions = ['port-security']
+
     @test.attr(type='negative')
     @decorators.idempotent_id('d39a96e2-2dea-4feb-8093-e7ac991ce6f8')
-    @test.requires_ext(extension='port-security', service='network')
     def test_create_port_security_false_on_shared_network(self):
         network = self.create_shared_network()
         self.assertTrue(network['shared'])
diff --git a/neutron/tests/tempest/api/admin/test_external_network_extension.py b/neutron/tests/tempest/api/admin/test_external_network_extension.py
index c642c03..bcdb0bd 100644
--- a/neutron/tests/tempest/api/admin/test_external_network_extension.py
+++ b/neutron/tests/tempest/api/admin/test_external_network_extension.py
@@ -14,7 +14,6 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 import testtools
 
 from neutron.tests.tempest.api import base
@@ -23,9 +22,9 @@
 class ExternalNetworksRBACTestJSON(base.BaseAdminNetworkTest):
 
     credentials = ['primary', 'alt', 'admin']
+    required_extensions = ['rbac-policies']
 
     @classmethod
-    @test.requires_ext(extension="rbac-policies", service="network")
     def resource_setup(cls):
         super(ExternalNetworksRBACTestJSON, cls).resource_setup()
         cls.client2 = cls.alt_manager.network_client
diff --git a/neutron/tests/tempest/api/admin/test_l3_agent_scheduler.py b/neutron/tests/tempest/api/admin/test_l3_agent_scheduler.py
index 32fccab..01d3846 100644
--- a/neutron/tests/tempest/api/admin/test_l3_agent_scheduler.py
+++ b/neutron/tests/tempest/api/admin/test_l3_agent_scheduler.py
@@ -14,7 +14,6 @@
 
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base
 from neutron.tests.tempest import exceptions
@@ -42,10 +41,7 @@
     The l3_agent_scheduler extension is required for these tests.
     """
 
-    @classmethod
-    @test.requires_ext(extension="l3_agent_scheduler", service="network")
-    def skip_checks(cls):
-        super(L3AgentSchedulerTestJSON, cls).skip_checks()
+    required_extensions = ['l3_agent_scheduler']
 
     @classmethod
     def resource_setup(cls):
diff --git a/neutron/tests/tempest/api/admin/test_quotas.py b/neutron/tests/tempest/api/admin/test_quotas.py
index ee53aa8..8091788 100644
--- a/neutron/tests/tempest/api/admin/test_quotas.py
+++ b/neutron/tests/tempest/api/admin/test_quotas.py
@@ -26,8 +26,9 @@
 
 class QuotasTestBase(base.BaseAdminNetworkTest):
 
+    required_extensions = ['quotas']
+
     @classmethod
-    @test.requires_ext(extension="quotas", service="network")
     def resource_setup(cls):
         if not CONF.identity_feature_enabled.api_v2_admin:
             # TODO(ihrachys) adopt to v3
diff --git a/neutron/tests/tempest/api/admin/test_routers_dvr.py b/neutron/tests/tempest/api/admin/test_routers_dvr.py
index f0904c1..5fa22cc 100644
--- a/neutron/tests/tempest/api/admin/test_routers_dvr.py
+++ b/neutron/tests/tempest/api/admin/test_routers_dvr.py
@@ -15,16 +15,15 @@
 
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base_routers as base
 
 
 class RoutersTestDVR(base.BaseRouterTest):
 
+    required_extensions = ['router', 'dvr']
+
     @classmethod
-    @test.requires_ext(extension="router", service="network")
-    @test.requires_ext(extension="dvr", service="network")
     def resource_setup(cls):
         # The check above will pass if api_extensions=all, which does
         # not mean DVR extension itself is present.
diff --git a/neutron/tests/tempest/api/admin/test_routers_flavors.py b/neutron/tests/tempest/api/admin/test_routers_flavors.py
index 5e81c83..d0c2f28 100644
--- a/neutron/tests/tempest/api/admin/test_routers_flavors.py
+++ b/neutron/tests/tempest/api/admin/test_routers_flavors.py
@@ -14,7 +14,6 @@
 from neutron_lib import constants
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 import testtools
 
 from neutron.tests.tempest.api import base_routers as base
@@ -22,12 +21,7 @@
 
 class RoutersFlavorTestCase(base.BaseRouterTest):
 
-    @classmethod
-    @test.requires_ext(extension="router", service="network")
-    @test.requires_ext(extension="flavors", service="network")
-    @test.requires_ext(extension="l3-flavors", service="network")
-    def skip_checks(cls):
-        super(RoutersFlavorTestCase, cls).skip_checks()
+    required_extensions = ['router', 'flavors', 'l3-flavors']
 
     @classmethod
     def resource_setup(cls):
diff --git a/neutron/tests/tempest/api/admin/test_routers_ha.py b/neutron/tests/tempest/api/admin/test_routers_ha.py
index 187eb6d..644ac18 100644
--- a/neutron/tests/tempest/api/admin/test_routers_ha.py
+++ b/neutron/tests/tempest/api/admin/test_routers_ha.py
@@ -12,16 +12,15 @@
 
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base_routers as base
 
 
 class RoutersTestHA(base.BaseRouterTest):
 
+    required_extensions = ['router', 'l3-ha']
+
     @classmethod
-    @test.requires_ext(extension="router", service="network")
-    @test.requires_ext(extension="l3-ha", service="network")
     def resource_setup(cls):
         # The check above will pass if api_extensions=all, which does
         # not mean "l3-ha" extension itself is present.
diff --git a/neutron/tests/tempest/api/admin/test_shared_network_extension.py b/neutron/tests/tempest/api/admin/test_shared_network_extension.py
index 5557c24..341ba28 100644
--- a/neutron/tests/tempest/api/admin/test_shared_network_extension.py
+++ b/neutron/tests/tempest/api/admin/test_shared_network_extension.py
@@ -148,11 +148,7 @@
 
 class AllowedAddressPairSharedNetworkTest(base.BaseAdminNetworkTest):
     allowed_address_pairs = [{'ip_address': '1.1.1.1'}]
-
-    @classmethod
-    @test.requires_ext(extension="allowed-address-pairs", service="network")
-    def skip_checks(cls):
-        super(AllowedAddressPairSharedNetworkTest, cls).skip_checks()
+    required_extensions = ['allowed-address-pairs']
 
     @classmethod
     def resource_setup(cls):
@@ -178,9 +174,9 @@
 
     force_tenant_isolation = True
     credentials = ['primary', 'alt', 'admin']
+    required_extensions = ['rbac-policies']
 
     @classmethod
-    @test.requires_ext(extension="rbac-policies", service="network")
     def resource_setup(cls):
         super(RBACSharedNetworksTest, cls).resource_setup()
         cls.client2 = cls.alt_manager.network_client
diff --git a/neutron/tests/tempest/api/base.py b/neutron/tests/tempest/api/base.py
index 55ad355..8213277 100644
--- a/neutron/tests/tempest/api/base.py
+++ b/neutron/tests/tempest/api/base.py
@@ -76,6 +76,10 @@
             raise cls.skipException("Neutron support is required")
         if cls._ip_version == 6 and not CONF.network_feature_enabled.ipv6:
             raise cls.skipException("IPv6 Tests are disabled.")
+        for req_ext in getattr(cls, 'required_extensions', []):
+            if not test.is_extension_enabled(req_ext, 'network'):
+                msg = "%s extension not enabled." % req_ext
+                raise cls.skipException(msg)
 
     @classmethod
     def setup_credentials(cls):
diff --git a/neutron/tests/tempest/api/test_address_scopes.py b/neutron/tests/tempest/api/test_address_scopes.py
index ea0b5da..d4474e4 100644
--- a/neutron/tests/tempest/api/test_address_scopes.py
+++ b/neutron/tests/tempest/api/test_address_scopes.py
@@ -25,10 +25,7 @@
 
 class AddressScopeTestBase(base.BaseAdminNetworkTest):
 
-    @classmethod
-    @test.requires_ext(extension="address-scope", service="network")
-    def resource_setup(cls):
-        super(AddressScopeTestBase, cls).resource_setup()
+    required_extensions = ['address-scope']
 
     def _create_address_scope(self, is_admin=False, **kwargs):
         name = data_utils.rand_name(ADDRESS_SCOPE_NAME)
diff --git a/neutron/tests/tempest/api/test_allowed_address_pair.py b/neutron/tests/tempest/api/test_allowed_address_pair.py
index 3c47e12..d339a26 100644
--- a/neutron/tests/tempest/api/test_allowed_address_pair.py
+++ b/neutron/tests/tempest/api/test_allowed_address_pair.py
@@ -15,7 +15,6 @@
 
 import netaddr
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base
 from neutron.tests.tempest import config
@@ -39,8 +38,9 @@
         api_extensions
     """
 
+    required_extensions = ['allowed-address-pairs']
+
     @classmethod
-    @test.requires_ext(extension="allowed-address-pairs", service="network")
     def resource_setup(cls):
         super(AllowedAddressPairTestJSON, cls).resource_setup()
         cls.network = cls.create_network()
diff --git a/neutron/tests/tempest/api/test_auto_allocated_topology.py b/neutron/tests/tempest/api/test_auto_allocated_topology.py
index 0db7602..0f0de54 100644
--- a/neutron/tests/tempest/api/test_auto_allocated_topology.py
+++ b/neutron/tests/tempest/api/test_auto_allocated_topology.py
@@ -15,7 +15,6 @@
 
 from oslo_config import cfg
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base
 
@@ -31,11 +30,7 @@
     # all tests are added under TestAutoAllocatedTopology,
     # nothing bad should happen.
     force_tenant_isolation = True
-
-    @classmethod
-    @test.requires_ext(extension="auto-allocated-topology", service="network")
-    def skip_checks(cls):
-        super(TestAutoAllocatedTopology, cls).skip_checks()
+    required_extensions = ['auto-allocated-topology']
 
     @classmethod
     def resource_setup(cls):
diff --git a/neutron/tests/tempest/api/test_extra_dhcp_options.py b/neutron/tests/tempest/api/test_extra_dhcp_options.py
index 7ba6bee..eeb3487 100644
--- a/neutron/tests/tempest/api/test_extra_dhcp_options.py
+++ b/neutron/tests/tempest/api/test_extra_dhcp_options.py
@@ -15,7 +15,6 @@
 
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base
 
@@ -36,8 +35,9 @@
     section of etc/tempest.conf
     """
 
+    required_extensions = ['extra_dhcp_opt']
+
     @classmethod
-    @test.requires_ext(extension="extra_dhcp_opt", service="network")
     def resource_setup(cls):
         super(ExtraDHCPOptionsTestJSON, cls).resource_setup()
         cls.network = cls.create_network()
diff --git a/neutron/tests/tempest/api/test_flavors_extensions.py b/neutron/tests/tempest/api/test_flavors_extensions.py
index ab3c522..764f3ed 100644
--- a/neutron/tests/tempest/api/test_flavors_extensions.py
+++ b/neutron/tests/tempest/api/test_flavors_extensions.py
@@ -14,7 +14,6 @@
 
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 from neutron.tests.tempest.api import base
 
@@ -29,8 +28,9 @@
         List, Show, Create, Update, Delete service profiles
     """
 
+    required_extensions = ['flavors']
+
     @classmethod
-    @test.requires_ext(extension="flavors", service="network")
     def resource_setup(cls):
         super(TestFlavorsJson, cls).resource_setup()
 
diff --git a/neutron/tests/tempest/api/test_floating_ips.py b/neutron/tests/tempest/api/test_floating_ips.py
index f261e37..b1511af 100644
--- a/neutron/tests/tempest/api/test_floating_ips.py
+++ b/neutron/tests/tempest/api/test_floating_ips.py
@@ -25,8 +25,9 @@
 
 class FloatingIPTestJSON(base.BaseNetworkTest):
 
+    required_extensions = ['router']
+
     @classmethod
-    @test.requires_ext(extension="router", service="network")
     def resource_setup(cls):
         super(FloatingIPTestJSON, cls).resource_setup()
         cls.ext_net_id = CONF.network.public_network_id
diff --git a/neutron/tests/tempest/api/test_floating_ips_negative.py b/neutron/tests/tempest/api/test_floating_ips_negative.py
index 39aaeb4..53e1523 100644
--- a/neutron/tests/tempest/api/test_floating_ips_negative.py
+++ b/neutron/tests/tempest/api/test_floating_ips_negative.py
@@ -27,8 +27,9 @@
 
 class FloatingIPNegativeTestJSON(base.BaseNetworkTest):
 
+    required_extensions = ['router']
+
     @classmethod
-    @test.requires_ext(extension="router", service="network")
     def resource_setup(cls):
         super(FloatingIPNegativeTestJSON, cls).resource_setup()
         cls.ext_net_id = CONF.network.public_network_id
diff --git a/neutron/tests/tempest/api/test_metering_extensions.py b/neutron/tests/tempest/api/test_metering_extensions.py
index 39e2645..2d95072 100644
--- a/neutron/tests/tempest/api/test_metering_extensions.py
+++ b/neutron/tests/tempest/api/test_metering_extensions.py
@@ -15,7 +15,6 @@
 from neutron_lib.db import constants as db_const
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base
 
@@ -32,8 +31,9 @@
         List, Show, Create, Delete Metering labels rules
     """
 
+    required_extensions = ['metering']
+
     @classmethod
-    @test.requires_ext(extension="metering", service="network")
     def resource_setup(cls):
         super(MeteringTestJSON, cls).resource_setup()
         description = "metering label created by tempest"
diff --git a/neutron/tests/tempest/api/test_metering_negative.py b/neutron/tests/tempest/api/test_metering_negative.py
index 87d7f93..cbdf74d 100644
--- a/neutron/tests/tempest/api/test_metering_negative.py
+++ b/neutron/tests/tempest/api/test_metering_negative.py
@@ -24,10 +24,7 @@
 
 class MeteringNegativeTestJSON(base.BaseAdminNetworkTest):
 
-    @classmethod
-    @test.requires_ext(extension="metering", service="network")
-    def resource_setup(cls):
-        super(MeteringNegativeTestJSON, cls).resource_setup()
+    required_extensions = ['metering']
 
     @test.attr(type='negative')
     @decorators.idempotent_id('8b3f7c84-9d37-4771-8681-bfd2c07f3c2d')
diff --git a/neutron/tests/tempest/api/test_qos.py b/neutron/tests/tempest/api/test_qos.py
index 287207e..93f96b7 100644
--- a/neutron/tests/tempest/api/test_qos.py
+++ b/neutron/tests/tempest/api/test_qos.py
@@ -28,10 +28,8 @@
 
 
 class QosTestJSON(base.BaseAdminNetworkTest):
-    @classmethod
-    @test.requires_ext(extension="qos", service="network")
-    def resource_setup(cls):
-        super(QosTestJSON, cls).resource_setup()
+
+    required_extensions = ['qos']
 
     @decorators.idempotent_id('108fbdf7-3463-4e47-9871-d07f3dcf5bbb')
     def test_create_policy(self):
@@ -368,9 +366,9 @@
 class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
 
     direction = None
+    required_extensions = ['qos']
 
     @classmethod
-    @test.requires_ext(extension="qos", service="network")
     @base.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
     def resource_setup(cls):
         super(QosBandwidthLimitRuleTestJSON, cls).resource_setup()
@@ -552,24 +550,23 @@
 class QosBandwidthLimitRuleWithDirectionTestJSON(
     QosBandwidthLimitRuleTestJSON):
 
+    required_extensions = (
+        QosBandwidthLimitRuleTestJSON.required_extensions +
+        ['qos-bw-limit-direction']
+    )
     scenarios = [
         ('ingress', {'direction': 'ingress'}),
         ('egress', {'direction': 'egress'}),
     ]
 
-    @classmethod
-    @test.requires_ext(extension="qos-bw-limit-direction", service="network")
-    def resource_setup(cls):
-        super(QosBandwidthLimitRuleWithDirectionTestJSON, cls).resource_setup()
-
 
 class RbacSharedQosPoliciesTest(base.BaseAdminNetworkTest):
 
     force_tenant_isolation = True
     credentials = ['primary', 'alt', 'admin']
+    required_extensions = ['qos']
 
     @classmethod
-    @test.requires_ext(extension="qos", service="network")
     def resource_setup(cls):
         super(RbacSharedQosPoliciesTest, cls).resource_setup()
         cls.client2 = cls.alt_manager.network_client
@@ -821,8 +818,9 @@
     VALID_DSCP_MARK1 = 56
     VALID_DSCP_MARK2 = 48
 
+    required_extensions = ['qos']
+
     @classmethod
-    @test.requires_ext(extension="qos", service="network")
     @base.require_qos_rule_type(qos_consts.RULE_TYPE_DSCP_MARKING)
     def resource_setup(cls):
         super(QosDscpMarkingRuleTestJSON, cls).resource_setup()
@@ -954,9 +952,9 @@
     DIRECTION_INGRESS = "ingress"
     RULE_NAME = qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH + "_rule"
     RULES_NAME = RULE_NAME + "s"
+    required_extensions = ['qos']
 
     @classmethod
-    @test.requires_ext(extension="qos", service="network")
     @base.require_qos_rule_type(qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH)
     def resource_setup(cls):
         super(QosMinimumBandwidthRuleTestJSON, cls).resource_setup()
@@ -1116,8 +1114,9 @@
     list_kwargs = {'description': 'search-criteria-test'}
     list_as_admin = True
 
+    required_extensions = ['qos']
+
     @classmethod
-    @test.requires_ext(extension="qos", service="network")
     def resource_setup(cls):
         super(QosSearchCriteriaTest, cls).resource_setup()
         for name in cls.resource_names:
diff --git a/neutron/tests/tempest/api/test_qos_negative.py b/neutron/tests/tempest/api/test_qos_negative.py
index 05a5512..3bc6088 100644
--- a/neutron/tests/tempest/api/test_qos_negative.py
+++ b/neutron/tests/tempest/api/test_qos_negative.py
@@ -23,10 +23,8 @@
 
 
 class QosNegativeTestJSON(base.BaseAdminNetworkTest):
-    @classmethod
-    @test.requires_ext(extension="qos", service="network")
-    def resource_setup(cls):
-        super(QosNegativeTestJSON, cls).resource_setup()
+
+    required_extensions = ['qos']
 
     @test.attr(type='negative')
     @decorators.idempotent_id('b9dce555-d3b3-11e5-950a-54ee757c77da')
diff --git a/neutron/tests/tempest/api/test_revisions.py b/neutron/tests/tempest/api/test_revisions.py
index 7a6443c..d94aede 100644
--- a/neutron/tests/tempest/api/test_revisions.py
+++ b/neutron/tests/tempest/api/test_revisions.py
@@ -22,10 +22,7 @@
 
 class TestRevisions(base.BaseAdminNetworkTest, bsg.BaseSecGroupTest):
 
-    @classmethod
-    @test.requires_ext(extension="standard-attr-revisions", service="network")
-    def skip_checks(cls):
-        super(TestRevisions, cls).skip_checks()
+    required_extensions = ['standard-attr-revisions']
 
     @decorators.idempotent_id('4a26a4be-9c53-483c-bc50-b53f1db10ac6')
     def test_update_network_bumps_revision(self):
diff --git a/neutron/tests/tempest/api/test_routers.py b/neutron/tests/tempest/api/test_routers.py
index 85bfe82..4275729 100644
--- a/neutron/tests/tempest/api/test_routers.py
+++ b/neutron/tests/tempest/api/test_routers.py
@@ -28,10 +28,7 @@
 
 class RoutersTest(base_routers.BaseRouterTest):
 
-    @classmethod
-    @test.requires_ext(extension="router", service="network")
-    def skip_checks(cls):
-        super(RoutersTest, cls).skip_checks()
+    required_extensions = ['router']
 
     @classmethod
     def resource_setup(cls):
@@ -231,10 +228,7 @@
 
 class DvrRoutersTest(base_routers.BaseRouterTest):
 
-    @classmethod
-    @test.requires_ext(extension="dvr", service="network")
-    def skip_checks(cls):
-        super(DvrRoutersTest, cls).skip_checks()
+    required_extensions = ['dvr']
 
     @decorators.idempotent_id('141297aa-3424-455d-aa8d-f2d95731e00a')
     def test_create_distributed_router(self):
@@ -261,10 +255,7 @@
 
 class HaRoutersTest(base_routers.BaseRouterTest):
 
-    @classmethod
-    @test.requires_ext(extension="l3-ha", service="network")
-    def skip_checks(cls):
-        super(HaRoutersTest, cls).skip_checks()
+    required_extensions = ['l3-ha']
 
     @decorators.idempotent_id('77db8eae-3aa3-4e61-bf2a-e739ce042e53')
     def test_convert_legacy_router(self):
@@ -281,14 +272,10 @@
 
 class RoutersSearchCriteriaTest(base.BaseSearchCriteriaTest):
 
+    required_extensions = ['router']
     resource = 'router'
 
     @classmethod
-    @test.requires_ext(extension="router", service="network")
-    def skip_checks(cls):
-        super(RoutersSearchCriteriaTest, cls).skip_checks()
-
-    @classmethod
     def resource_setup(cls):
         super(RoutersSearchCriteriaTest, cls).resource_setup()
         for name in cls.resource_names:
diff --git a/neutron/tests/tempest/api/test_routers_negative.py b/neutron/tests/tempest/api/test_routers_negative.py
index b97e30d..26f7298 100644
--- a/neutron/tests/tempest/api/test_routers_negative.py
+++ b/neutron/tests/tempest/api/test_routers_negative.py
@@ -24,10 +24,7 @@
 
 class RoutersNegativeTestBase(base.BaseRouterTest):
 
-    @classmethod
-    @test.requires_ext(extension="router", service="network")
-    def skip_checks(cls):
-        super(RoutersNegativeTestBase, cls).skip_checks()
+    required_extensions = ['router']
 
     @classmethod
     def resource_setup(cls):
@@ -74,10 +71,7 @@
 
 class DvrRoutersNegativeTest(RoutersNegativeTestBase):
 
-    @classmethod
-    @test.requires_ext(extension="dvr", service="network")
-    def skip_checks(cls):
-        super(DvrRoutersNegativeTest, cls).skip_checks()
+    required_extensions = ['dvr']
 
     @test.attr(type='negative')
     @decorators.idempotent_id('4990b055-8fc7-48ab-bba7-aa28beaad0b9')
@@ -89,10 +83,7 @@
 
 class HaRoutersNegativeTest(RoutersNegativeTestBase):
 
-    @classmethod
-    @test.requires_ext(extension="l3-ha", service="network")
-    def skip_checks(cls):
-        super(HaRoutersNegativeTest, cls).skip_checks()
+    required_extensions = ['l3-ha']
 
     @test.attr(type='negative')
     @decorators.idempotent_id('821b85b9-9c51-40f3-831f-bf223a7e0084')
diff --git a/neutron/tests/tempest/api/test_security_groups.py b/neutron/tests/tempest/api/test_security_groups.py
index a99cd51..5ff905d 100644
--- a/neutron/tests/tempest/api/test_security_groups.py
+++ b/neutron/tests/tempest/api/test_security_groups.py
@@ -15,17 +15,13 @@
 
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base_security_groups as base
 
 
 class SecGroupTest(base.BaseSecGroupTest):
 
-    @classmethod
-    @test.requires_ext(extension="security-group", service="network")
-    def resource_setup(cls):
-        super(SecGroupTest, cls).resource_setup()
+    required_extensions = ['security-group']
 
     @decorators.idempotent_id('bfd128e5-3c92-44b6-9d66-7fe29d22c802')
     def test_create_list_update_show_delete_security_group(self):
diff --git a/neutron/tests/tempest/api/test_security_groups_negative.py b/neutron/tests/tempest/api/test_security_groups_negative.py
index 1d841b1..c68e0b9 100644
--- a/neutron/tests/tempest/api/test_security_groups_negative.py
+++ b/neutron/tests/tempest/api/test_security_groups_negative.py
@@ -25,10 +25,7 @@
 
 class NegativeSecGroupTest(base.BaseSecGroupTest):
 
-    @classmethod
-    @test.requires_ext(extension="security-group", service="network")
-    def resource_setup(cls):
-        super(NegativeSecGroupTest, cls).resource_setup()
+    required_extensions = ['security-group']
 
     @test.attr(type='negative')
     @decorators.idempotent_id('594edfa8-9a5b-438e-9344-49aece337d49')
diff --git a/neutron/tests/tempest/api/test_service_type_management.py b/neutron/tests/tempest/api/test_service_type_management.py
index 5d2d070..3ca7250 100644
--- a/neutron/tests/tempest/api/test_service_type_management.py
+++ b/neutron/tests/tempest/api/test_service_type_management.py
@@ -11,17 +11,13 @@
 #    under the License.
 
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base
 
 
 class ServiceTypeManagementTest(base.BaseNetworkTest):
 
-    @classmethod
-    @test.requires_ext(extension="service-type", service="network")
-    def resource_setup(cls):
-        super(ServiceTypeManagementTest, cls).resource_setup()
+    required_extensions = ['service-type']
 
     @decorators.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6')
     def test_service_provider_list(self):
diff --git a/neutron/tests/tempest/api/test_tag.py b/neutron/tests/tempest/api/test_tag.py
index 2553dfb..dc78e60 100644
--- a/neutron/tests/tempest/api/test_tag.py
+++ b/neutron/tests/tempest/api/test_tag.py
@@ -19,8 +19,9 @@
 
 class TagTestJSON(base.BaseAdminNetworkTest):
 
+    required_extensions = ['tag']
+
     @classmethod
-    @test.requires_ext(extension="tag", service="network")
     def resource_setup(cls):
         super(TagTestJSON, cls).resource_setup()
         cls.res_id = cls._create_resource()
@@ -154,9 +155,9 @@
 
 class TagFilterTestJSON(base.BaseAdminNetworkTest):
     credentials = ['primary', 'alt', 'admin']
+    required_extensions = ['tag']
 
     @classmethod
-    @test.requires_ext(extension="tag", service="network")
     def resource_setup(cls):
         super(TagFilterTestJSON, cls).resource_setup()
 
@@ -323,10 +324,7 @@
 
 class UpdateTagsTest(base.BaseAdminNetworkTest):
 
-    @classmethod
-    @test.requires_ext(extension="tag", service="network")
-    def resource_setup(cls):
-        super(UpdateTagsTest, cls).resource_setup()
+    required_extensions = ['tag']
 
     def _get_and_compare_tags(self, tags, res_id):
         # nothing specific about networks here, just a resource that is
diff --git a/neutron/tests/tempest/api/test_timestamp.py b/neutron/tests/tempest/api/test_timestamp.py
index 8b57b2d..2406922 100644
--- a/neutron/tests/tempest/api/test_timestamp.py
+++ b/neutron/tests/tempest/api/test_timestamp.py
@@ -14,7 +14,6 @@
 
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 from neutron.tests.tempest.api import base
 from neutron.tests.tempest.api import base_routers
@@ -26,6 +25,8 @@
 
 class TestTimeStamp(base.BaseAdminNetworkTest):
 
+    required_extensions = ["standard-attr-timestamp"]
+
     ## attributes for subnetpool
     min_prefixlen = '28'
     max_prefixlen = '31'
@@ -35,11 +36,6 @@
     larger_prefix = '10.11.0.0/16'
 
     @classmethod
-    @test.requires_ext(extension="standard-attr-timestamp", service="network")
-    def skip_checks(cls):
-        super(TestTimeStamp, cls).skip_checks()
-
-    @classmethod
     def resource_setup(cls):
         super(TestTimeStamp, cls).resource_setup()
         prefixes = ['10.11.12.0/24']
@@ -183,13 +179,8 @@
 
 
 class TestTimeStampWithL3(base_routers.BaseRouterTest):
-    @classmethod
-    def skip_checks(cls):
-        super(TestTimeStampWithL3, cls).skip_checks()
 
-        if not test.is_extension_enabled('standard-attr-timestamp', 'network'):
-            raise cls.skipException("standard-attr-timestamp extension not "
-                                    "enabled")
+    required_extensions = ['standard-attr-timestamp']
 
     @classmethod
     def resource_setup(cls):
@@ -260,13 +251,8 @@
 
 
 class TestTimeStampWithSecurityGroup(base_security_groups.BaseSecGroupTest):
-    @classmethod
-    def skip_checks(cls):
-        super(TestTimeStampWithSecurityGroup, cls).skip_checks()
 
-        if not test.is_extension_enabled('standard-attr-timestamp', 'network'):
-            raise cls.skipException("standard-attr-timestamp extension not "
-                                    "enabled")
+    required_extensions = ['standard-attr-timestamp']
 
     @classmethod
     def resource_setup(cls):
diff --git a/neutron/tests/tempest/api/test_trunk.py b/neutron/tests/tempest/api/test_trunk.py
index 2617eba..3e035a0 100644
--- a/neutron/tests/tempest/api/test_trunk.py
+++ b/neutron/tests/tempest/api/test_trunk.py
@@ -38,20 +38,13 @@
 
 class TrunkTestJSONBase(base.BaseAdminNetworkTest):
 
-    extension = 'trunk'
+    required_extensions = ['trunk']
 
     def setUp(self):
         self.addCleanup(self.resource_cleanup)
         super(TrunkTestJSONBase, self).setUp()
 
     @classmethod
-    def skip_checks(cls):
-        super(TrunkTestJSONBase, cls).skip_checks()
-        if not test.is_extension_enabled(cls.extension, 'network'):
-            msg = "%s extension not enabled." % cls.extension
-            raise cls.skipException(msg)
-
-    @classmethod
     def resource_setup(cls):
         super(TrunkTestJSONBase, cls).resource_setup()
         cls.trunks = []
@@ -227,10 +220,6 @@
     @classmethod
     def skip_checks(cls):
         super(TrunkTestInheritJSONBase, cls).skip_checks()
-        for ext in cls.required_extensions:
-            if not test.is_extension_enabled(ext, 'network'):
-                msg = "%s extension not enabled." % ext
-                raise cls.skipException(msg)
         if ("vlan" not in
                 config.CONF.neutron_plugin_options.available_type_drivers):
             raise cls.skipException("VLAN type_driver is not enabled")
@@ -277,11 +266,6 @@
     @classmethod
     def skip_checks(cls):
         super(TrunkTestMtusJSONBase, cls).skip_checks()
-        for ext in cls.required_extensions:
-            if not test.is_extension_enabled(ext, 'network'):
-                msg = "%s extension not enabled." % ext
-                raise cls.skipException(msg)
-
         if any(t
                not in config.CONF.neutron_plugin_options.available_type_drivers
                for t in ['gre', 'vxlan']):
@@ -351,16 +335,10 @@
 
 class TrunksSearchCriteriaTest(base.BaseSearchCriteriaTest):
 
+    required_extensions = ['trunk']
     resource = 'trunk'
 
     @classmethod
-    def skip_checks(cls):
-        super(TrunksSearchCriteriaTest, cls).skip_checks()
-        if not test.is_extension_enabled('trunk', 'network'):
-            msg = "trunk extension not enabled."
-            raise cls.skipException(msg)
-
-    @classmethod
     def resource_setup(cls):
         super(TrunksSearchCriteriaTest, cls).resource_setup()
         cls.trunks = []
diff --git a/neutron/tests/tempest/api/test_trunk_details.py b/neutron/tests/tempest/api/test_trunk_details.py
index 2261b1c..b51025b 100644
--- a/neutron/tests/tempest/api/test_trunk_details.py
+++ b/neutron/tests/tempest/api/test_trunk_details.py
@@ -19,7 +19,7 @@
 
 class TestTrunkDetailsJSON(test_trunk.TrunkTestJSONBase):
 
-    extension = 'trunk-details'
+    required_extensions = ['trunk-details']
 
     @decorators.idempotent_id('f0bed24f-d36a-498b-b4e7-0d66e3fb7308')
     def test_port_resource_trunk_details_no_subports(self):