Merge "Tempest: Add default-subnetpools tests"
diff --git a/neutron/tests/tempest/README.rst b/neutron/tests/tempest/README.rst
index d61e72c..f27c34e 100644
--- a/neutron/tests/tempest/README.rst
+++ b/neutron/tests/tempest/README.rst
@@ -1,10 +1,9 @@
 WARNING
 =======
 
-The files under this path were copied from tempest as part of the move
-of the api tests, and they will be removed as required over time to
-minimize the dependency on the tempest testing framework.
-While it exists, only neutron.tests.tempest.api and neutron.tests.retargetable
-should be importing files from this path.  neutron.tests.tempest.config uses
-the global cfg.CONF instance and importing it outside of the api tests
-has the potential to break Neutron's use of cfg.CONF.
+Some files under this path were copied from tempest as part of the move of the
+api tests, and they will be removed as required over time to minimize the
+dependency on the tempest testing framework.  While it exists, only
+neutron.tests.tempest.* should be importing files from this path.
+neutron.tests.tempest.config uses the global cfg.CONF instance and importing it
+outside of the api tests has the potential to break Neutron's use of cfg.CONF.
diff --git a/neutron/tests/tempest/api/admin/test_agent_management.py b/neutron/tests/tempest/api/admin/test_agent_management.py
index 08f2bc3..ceda460 100644
--- a/neutron/tests/tempest/api/admin/test_agent_management.py
+++ b/neutron/tests/tempest/api/admin/test_agent_management.py
@@ -14,21 +14,20 @@
 
 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()
         agents = body['agents']
         cls.agent = agents[0]  # don't modify this agent
-        cls.dyn_agent = agents[1]
 
     @decorators.idempotent_id('9c80f04d-11f3-44a4-8738-ed2f879b0ff4')
     def test_list_agent(self):
@@ -66,20 +65,26 @@
 
     @decorators.idempotent_id('68a94a14-1243-46e6-83bf-157627e31556')
     def test_update_agent_description(self):
+        agents = self.admin_client.list_agents()['agents']
+        try:
+            dyn_agent = agents[1]
+        except IndexError:
+            raise self.skipException("This test requires at least two agents.")
+
         self.useFixture(tempest_fixtures.LockFixture('agent_description'))
         description = 'description for update agent.'
         agent_description = {'description': description}
-        body = self.admin_client.update_agent(agent_id=self.dyn_agent['id'],
+        body = self.admin_client.update_agent(agent_id=dyn_agent['id'],
                                               agent_info=agent_description)
-        self.addCleanup(self._restore_agent)
+        self.addCleanup(self._restore_agent, dyn_agent)
         updated_description = body['agent']['description']
         self.assertEqual(updated_description, description)
 
-    def _restore_agent(self):
+    def _restore_agent(self, dyn_agent):
         """
         Restore the agent description after update test.
         """
-        description = self.dyn_agent['description']
+        description = dyn_agent['description']
         origin_agent = {'description': description}
-        self.admin_client.update_agent(agent_id=self.dyn_agent['id'],
+        self.admin_client.update_agent(agent_id=dyn_agent['id'],
                                        agent_info=origin_agent)
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..6f6c94d 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,12 +22,12 @@
 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
+        cls.client2 = cls.os_alt.network_client
 
     def _create_network(self, external=True):
         post_body = {'name': data_utils.rand_name('network-')}
diff --git a/neutron/tests/tempest/api/admin/test_floating_ips_admin_actions.py b/neutron/tests/tempest/api/admin/test_floating_ips_admin_actions.py
index 764da2c..e997380 100644
--- a/neutron/tests/tempest/api/admin/test_floating_ips_admin_actions.py
+++ b/neutron/tests/tempest/api/admin/test_floating_ips_admin_actions.py
@@ -34,7 +34,7 @@
         super(FloatingIPAdminTestJSON, cls).resource_setup()
         cls.ext_net_id = CONF.network.public_network_id
         cls.floating_ip = cls.create_floatingip(cls.ext_net_id)
-        cls.alt_client = cls.alt_manager.network_client
+        cls.alt_client = cls.os_alt.network_client
         cls.network = cls.create_network()
         cls.subnet = cls.create_subnet(cls.network)
         cls.router = cls.create_router(data_utils.rand_name('router-'),
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..8f03cc6 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.
@@ -91,10 +90,12 @@
         name = data_utils.rand_name('router')
         # router needs to be in admin state down in order to be upgraded to DVR
         router = self.admin_client.create_router(name, distributed=False,
+                                                 ha=False,
                                                  admin_state_up=False)
         self.addCleanup(self.admin_client.delete_router,
                         router['router']['id'])
         self.assertFalse(router['router']['distributed'])
+        self.assertFalse(router['router']['ha'])
         router = self.admin_client.update_router(router['router']['id'],
                                                  distributed=True)
         self.assertTrue(router['router']['distributed'])
diff --git a/neutron/tests/tempest/api/admin/test_routers_flavors.py b/neutron/tests/tempest/api/admin/test_routers_flavors.py
index 5e81c83..4153c2d 100644
--- a/neutron/tests/tempest/api/admin/test_routers_flavors.py
+++ b/neutron/tests/tempest/api/admin/test_routers_flavors.py
@@ -11,10 +11,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from neutron_lib import constants
+from neutron_lib.plugins 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..458a6f1 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,12 +174,12 @@
 
     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
+        cls.client2 = cls.os_alt.network_client
 
     def _make_admin_net_and_subnet_shared_to_tenant_id(self, tenant_id):
         net = self.admin_client.create_network(
diff --git a/neutron/tests/tempest/api/base.py b/neutron/tests/tempest/api/base.py
index 55ad355..d0b8382 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):
@@ -324,7 +328,7 @@
         ext_gw_info = {}
         if external_network_id:
             ext_gw_info['network_id'] = external_network_id
-        if enable_snat:
+        if enable_snat is not None:
             ext_gw_info['enable_snat'] = enable_snat
         body = client.create_router(
             router_name, external_gateway_info=ext_gw_info,
@@ -339,7 +343,7 @@
 
     @classmethod
     def create_admin_router(cls, *args, **kwargs):
-        return cls._create_router_with_client(cls.admin_manager.network_client,
+        return cls._create_router_with_client(cls.os_admin.network_client,
                                               *args, **kwargs)
 
     @classmethod
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 7280297..f35abd8 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):
@@ -82,7 +80,8 @@
     def test_policy_update(self):
         policy = self.create_qos_policy(name='test-policy',
                                         description='',
-                                        shared=False)
+                                        shared=False,
+                                        tenant_id=self.admin_client.tenant_id)
         self.admin_client.update_qos_policy(policy['id'],
                                             description='test policy desc2',
                                             shared=True)
@@ -119,7 +118,8 @@
     def test_shared_policy_update(self):
         policy = self.create_qos_policy(name='test-policy',
                                         description='',
-                                        shared=True)
+                                        shared=True,
+                                        tenant_id=self.admin_client.tenant_id)
 
         self.admin_client.update_qos_policy(policy['id'],
                                             description='test policy desc2')
@@ -156,29 +156,21 @@
 
     def _test_list_rule_types(self, client):
         # List supported rule types
-        # TODO(QoS): since in gate we run both ovs and linuxbridge ml2 drivers,
-        # and since Linux Bridge ml2 driver does not have QoS support yet, ml2
-        # plugin reports no rule types are supported. Once linuxbridge will
-        # receive support for QoS, the list of expected rule types will change.
+        # Since returned rule types depends on loaded backend drivers this test
+        # is checking only if returned keys are same as expected keys
         #
         # In theory, we could make the test conditional on which ml2 drivers
         # are enabled in gate (or more specifically, on which supported qos
         # rules are claimed by core plugin), but that option doesn't seem to be
         # available through tempest.lib framework
-        expected_rule_types = []
-        expected_rule_details = ['type']
+        expected_rule_keys = ['type']
 
         rule_types = client.list_qos_rule_types()
         actual_list_rule_types = rule_types['rule_types']
-        actual_rule_types = [rule['type'] for rule in actual_list_rule_types]
 
         # Verify that only required fields present in rule details
         for rule in actual_list_rule_types:
-            self.assertEqual(tuple(rule.keys()), tuple(expected_rule_details))
-
-        # Verify if expected rules are present in the actual rules list
-        for rule in expected_rule_types:
-            self.assertIn(rule, actual_rule_types)
+            self.assertEqual(tuple(expected_rule_keys), tuple(rule.keys()))
 
     def _disassociate_network(self, client, network_id):
         updated_network = client.update_network(network_id,
@@ -366,9 +358,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()
@@ -550,27 +542,26 @@
 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
+        cls.client2 = cls.os_alt.network_client
 
     def _create_qos_policy(self, tenant_id=None):
         args = {'name': data_utils.rand_name('test-policy'),
@@ -606,7 +597,8 @@
     def test_policy_sharing_with_wildcard(self):
         qos_pol = self.create_qos_policy(
             name=data_utils.rand_name('test-policy'),
-            description='test-shared-policy', shared=False)
+            description='test-shared-policy', shared=False,
+            tenant_id=self.admin_client.tenant_id)
         self.assertNotIn(qos_pol, self.client2.list_qos_policies()['policies'])
 
         # test update shared False -> True
@@ -818,8 +810,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()
@@ -951,9 +944,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()
@@ -1113,8 +1106,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..83c8410 100644
--- a/neutron/tests/tempest/api/test_revisions.py
+++ b/neutron/tests/tempest/api/test_revisions.py
@@ -13,6 +13,7 @@
 import netaddr
 
 from tempest.lib import decorators
+from tempest.lib import exceptions
 from tempest import test
 
 from neutron.tests.tempest.api import base
@@ -22,10 +23,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):
@@ -36,6 +34,35 @@
         self.assertGreater(updated['network']['revision_number'],
                            net['revision_number'])
 
+    @decorators.idempotent_id('4a26a4be-9c53-483c-bc50-b11111113333')
+    def test_update_network_constrained_by_revision(self):
+        net = self.create_network()
+        current = net['revision_number']
+        stale = current - 1
+        # using a stale number should fail
+        self.assertRaises(
+            exceptions.PreconditionFailed,
+            self.client.update_network,
+            net['id'], name='newnet',
+            headers={'If-Match': 'revision_number=%s' % stale}
+        )
+
+        # using current should pass. in case something is updating the network
+        # on the server at the same time, we have to re-read and update to be
+        # safe
+        for i in range(100):
+            current = (self.client.show_network(net['id'])
+                       ['network']['revision_number'])
+            try:
+                self.client.update_network(
+                    net['id'], name='newnet',
+                    headers={'If-Match': 'revision_number=%s' % current})
+            except exceptions.UnexpectedResponseCode:
+                continue
+            break
+        else:
+            self.fail("Failed to update network after 100 tries.")
+
     @decorators.idempotent_id('cac7ecde-12d5-4331-9a03-420899dea077')
     def test_update_port_bumps_revision(self):
         net = self.create_network()
diff --git a/neutron/tests/tempest/api/test_routers.py b/neutron/tests/tempest/api/test_routers.py
index 85bfe82..8572196 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):
@@ -248,8 +242,15 @@
 
     @decorators.idempotent_id('644d7a4a-01a1-4b68-bb8d-0c0042cb1729')
     def test_convert_centralized_router(self):
-        router = self._create_router(data_utils.rand_name('router'))
-        self.assertNotIn('distributed', router)
+        router_args = {'tenant_id': self.client.tenant_id,
+                       'distributed': False, 'ha': False}
+        router = self.admin_client.create_router(
+            data_utils.rand_name('router'), admin_state_up=False,
+            **router_args)['router']
+        self.addCleanup(self.admin_client.delete_router,
+                        router['id'])
+        self.assertFalse(router['distributed'])
+        self.assertFalse(router['ha'])
         update_body = self.admin_client.update_router(router['id'],
                                                       distributed=True)
         self.assertTrue(update_body['router']['distributed'])
@@ -257,14 +258,12 @@
         self.assertTrue(show_body['router']['distributed'])
         show_body = self.client.show_router(router['id'])
         self.assertNotIn('distributed', show_body['router'])
+        self.assertNotIn('ha', show_body['router'])
 
 
 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 +280,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..21b5a25 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):
@@ -57,7 +54,7 @@
     @test.attr(type='negative')
     @decorators.idempotent_id('159f576d-a423-46b5-b501-622694c02f6b')
     def test_add_interface_wrong_tenant(self):
-        client2 = self.alt_manager.network_client
+        client2 = self.os_alt.network_client
         network = client2.create_network()['network']
         self.addCleanup(client2.delete_network, network['id'])
         subnet = self.create_subnet(network, client=client2)
@@ -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 70a2d44..f819671 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()
@@ -44,6 +45,10 @@
         self.client.update_tag(self.resource, self.res_id, 'red')
         self._get_and_compare_tags(['red', 'blue', 'green'])
 
+        # add a tag with a dot
+        self.client.update_tag(self.resource, self.res_id, 'black.or.white')
+        self._get_and_compare_tags(['red', 'blue', 'green', 'black.or.white'])
+
         # replace tags
         tags = ['red', 'yellow', 'purple']
         res_body = self.client.update_tags(self.resource, self.res_id, tags)
@@ -150,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()
 
@@ -172,7 +177,7 @@
     @classmethod
     def setup_clients(cls):
         super(TagFilterTestJSON, cls).setup_clients()
-        cls.client = cls.alt_manager.network_client
+        cls.client = cls.os_alt.network_client
 
     def _assertEqualResources(self, expected, res):
         actual = [n['name'] for n in res if n['name'].startswith('tag-res')]
@@ -319,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..aef82e7 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 = []
@@ -115,18 +108,18 @@
     @decorators.idempotent_id('4ce46c22-a2b6-4659-bc5a-0ef2463cab32')
     def test_create_update_trunk(self):
         trunk = self._create_trunk_with_network_and_parent(None)
-        self.assertEqual(1, trunk['trunk']['revision_number'])
+        rev = trunk['trunk']['revision_number']
         trunk_id = trunk['trunk']['id']
         res = self._show_trunk(trunk_id)
         self.assertTrue(res['trunk']['admin_state_up'])
-        self.assertEqual(1, res['trunk']['revision_number'])
+        self.assertEqual(rev, res['trunk']['revision_number'])
         self.assertEqual("", res['trunk']['name'])
         self.assertEqual("", res['trunk']['description'])
         res = self.client.update_trunk(
             trunk_id, name='foo', admin_state_up=False)
         self.assertFalse(res['trunk']['admin_state_up'])
         self.assertEqual("foo", res['trunk']['name'])
-        self.assertGreater(res['trunk']['revision_number'], 1)
+        self.assertGreater(res['trunk']['revision_number'], rev)
         # enable the trunk so that it can be managed
         self.client.update_trunk(trunk_id, admin_state_up=True)
 
@@ -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):
diff --git a/neutron/tests/tempest/scenario/base.py b/neutron/tests/tempest/scenario/base.py
index 1e20c74..5ccbd1c 100644
--- a/neutron/tests/tempest/scenario/base.py
+++ b/neutron/tests/tempest/scenario/base.py
@@ -41,7 +41,7 @@
     @classmethod
     def resource_cleanup(cls):
         for keypair in cls.keypairs:
-            cls.manager.keypairs_client.delete_keypair(
+            cls.os_primary.keypairs_client.delete_keypair(
                 keypair_name=keypair['name'])
 
         super(BaseTempestTestCase, cls).resource_cleanup()
@@ -71,7 +71,7 @@
         if not security_groups:
             security_groups = [{'name': 'default'}]
 
-        server = self.manager.servers_client.create_server(
+        server = self.os_primary.servers_client.create_server(
             name=name,
             flavorRef=flavor_ref,
             imageRef=image_ref,
@@ -81,15 +81,15 @@
 
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
             waiters.wait_for_server_termination,
-            self.manager.servers_client, server['server']['id'])
+            self.os_primary.servers_client, server['server']['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.manager.servers_client.delete_server,
+                        self.os_primary.servers_client.delete_server,
                         server['server']['id'])
         return server
 
     @classmethod
     def create_keypair(cls, client=None):
-        client = client or cls.manager.keypairs_client
+        client = client or cls.os_primary.keypairs_client
         name = data_utils.rand_name('keypair-test')
         body = client.create_keypair(name=name)
         cls.keypairs.append(body['keypair'])
@@ -97,7 +97,7 @@
 
     @classmethod
     def create_secgroup_rules(cls, rule_list, secgroup_id=None):
-        client = cls.manager.network_client
+        client = cls.os_primary.network_client
         if not secgroup_id:
             sgs = client.list_security_groups()['security_groups']
             for sg in sgs:
@@ -155,7 +155,7 @@
         return router
 
     def create_and_associate_floatingip(self, port_id):
-        fip = self.manager.network_client.create_floatingip(
+        fip = self.os_primary.network_client.create_floatingip(
             CONF.network.public_network_id,
             port_id=port_id)['floatingip']
         self.floating_ips.append(fip)
@@ -172,7 +172,7 @@
         self.subnet = self.create_subnet(self.network)
         LOG.debug("Created subnet %s", self.subnet['id'])
 
-        secgroup = self.manager.network_client.create_security_group(
+        secgroup = self.os_primary.network_client.create_security_group(
             name=data_utils.rand_name('secgroup-'))
         LOG.debug("Created security group %s",
                   secgroup['security_group']['name'])
@@ -189,7 +189,7 @@
             key_name=self.keypair['name'],
             networks=[{'uuid': self.network['id']}],
             security_groups=[{'name': secgroup['security_group']['name']}])
-        waiters.wait_for_server_status(self.manager.servers_client,
+        waiters.wait_for_server_status(self.os_primary.servers_client,
                                        self.server['server']['id'],
                                        constants.SERVER_STATUS_ACTIVE)
         port = self.client.list_ports(network_id=self.network['id'],
@@ -211,12 +211,12 @@
             LOG.debug('Console output not supported, cannot log')
             return
         if not servers:
-            servers = self.manager.servers_client.list_servers()
+            servers = self.os_primary.servers_client.list_servers()
             servers = servers['servers']
         for server in servers:
             try:
                 console_output = (
-                    self.manager.servers_client.get_console_output(
+                    self.os_primary.servers_client.get_console_output(
                         server['id'])['output'])
                 LOG.debug('Console output for %s\nbody=\n%s',
                           server['id'], console_output)
diff --git a/neutron/tests/tempest/scenario/test_dvr.py b/neutron/tests/tempest/scenario/test_dvr.py
index 049aa8f..0b03276 100644
--- a/neutron/tests/tempest/scenario/test_dvr.py
+++ b/neutron/tests/tempest/scenario/test_dvr.py
@@ -40,7 +40,7 @@
         port_id = self.client.list_ports(
             network_id=network_id,
             device_owner=constants.DEVICE_OWNER_ROUTER_SNAT)['ports'][0]['id']
-        self.admin_manager.network_client.update_port(
+        self.os_admin.network_client.update_port(
             port_id, admin_state_up=False)
 
 
diff --git a/neutron/tests/tempest/scenario/test_floatingip.py b/neutron/tests/tempest/scenario/test_floatingip.py
index 1ccb6ac..fe6ce3e 100644
--- a/neutron/tests/tempest/scenario/test_floatingip.py
+++ b/neutron/tests/tempest/scenario/test_floatingip.py
@@ -19,6 +19,7 @@
 from tempest.lib import decorators
 from tempest import test
 import testscenarios
+from testscenarios.scenarios import multiply_scenarios
 
 from neutron.tests.tempest.common import ssh
 from neutron.tests.tempest import config
@@ -45,7 +46,7 @@
         cls.create_router_interface(cls.router['id'], cls.subnet['id'])
         cls.keypair = cls.create_keypair()
 
-        cls.secgroup = cls.manager.network_client.create_security_group(
+        cls.secgroup = cls.os_primary.network_client.create_security_group(
             name=data_utils.rand_name('secgroup-'))['security_group']
         cls.security_groups.append(cls.secgroup)
         cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
@@ -77,18 +78,34 @@
             image_ref=CONF.compute.image_ref,
             key_name=self.keypair['name'],
             networks=[{'port': port['id']}])['server']
-        waiters.wait_for_server_status(self.manager.servers_client,
+        waiters.wait_for_server_status(self.os_primary.servers_client,
                                        server['id'],
                                        constants.SERVER_STATUS_ACTIVE)
         return {'port': port, 'fip': fip, 'server': server}
 
     def _test_east_west(self):
+        # The proxy VM is used to control the source VM when it doesn't
+        # have a floating-ip.
+        if self.src_has_fip:
+            proxy = None
+            proxy_client = None
+        else:
+            proxy = self._create_server()
+            proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
+                                      CONF.validation.image_ssh_user,
+                                      pkey=self.keypair['private_key'])
+
         # Source VM
-        server1 = self._create_server()
-        server1_ip = server1['fip']['floating_ip_address']
-        ssh_client = ssh.Client(server1_ip,
+        if self.src_has_fip:
+            src_server = self._create_server()
+            src_server_ip = src_server['fip']['floating_ip_address']
+        else:
+            src_server = self._create_server(create_floating_ip=False)
+            src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
+        ssh_client = ssh.Client(src_server_ip,
                                 CONF.validation.image_ssh_user,
-                                pkey=self.keypair['private_key'])
+                                pkey=self.keypair['private_key'],
+                                proxy_client=proxy_client)
 
         # Destination VM
         if self.dest_has_fip:
@@ -107,11 +124,13 @@
 
 class FloatingIpSameNetwork(FloatingIpTestCasesMixin,
                             base.BaseTempestTestCase):
-    # REVISIT(yamamoto): 'SRC without FIP' case is possible?
-    scenarios = [
+    scenarios = multiply_scenarios([
+        ('SRC with FIP', dict(src_has_fip=True)),
+        ('SRC without FIP', dict(src_has_fip=False)),
+    ], [
         ('DEST with FIP', dict(dest_has_fip=True)),
         ('DEST without FIP', dict(dest_has_fip=False)),
-    ]
+    ])
 
     same_network = True
 
@@ -122,11 +141,13 @@
 
 class FloatingIpSeparateNetwork(FloatingIpTestCasesMixin,
                                 base.BaseTempestTestCase):
-    # REVISIT(yamamoto): 'SRC without FIP' case is possible?
-    scenarios = [
+    scenarios = multiply_scenarios([
+        ('SRC with FIP', dict(src_has_fip=True)),
+        ('SRC without FIP', dict(src_has_fip=False)),
+    ], [
         ('DEST with FIP', dict(dest_has_fip=True)),
         ('DEST without FIP', dict(dest_has_fip=False)),
-    ]
+    ])
 
     same_network = False
 
diff --git a/neutron/tests/tempest/scenario/test_migration.py b/neutron/tests/tempest/scenario/test_migration.py
index db89716..4951c61 100644
--- a/neutron/tests/tempest/scenario/test_migration.py
+++ b/neutron/tests/tempest/scenario/test_migration.py
@@ -32,7 +32,7 @@
         super(NetworkMigrationTestBase, cls).skip_checks()
 
     def _check_update(self, router, is_dvr, is_ha):
-        router = self.admin_manager.network_client.show_router(router['id'])
+        router = self.os_admin.network_client.show_router(router['id'])
         self.assertEqual(is_dvr, router['router']['distributed'])
         self.assertEqual(is_ha, router['router']['ha'])
 
@@ -44,13 +44,13 @@
         self.setup_network_and_server(router=router)
         self._check_connectivity()
 
-        self.admin_manager.network_client.update_router(
+        self.os_admin.network_client.update_router(
             router_id=router['id'], admin_state_up=False)
-        self.admin_manager.network_client.update_router(
+        self.os_admin.network_client.update_router(
             router_id=router['id'], distributed=after_dvr, ha=after_ha)
         self._check_update(router, after_dvr, after_ha)
 
-        self.admin_manager.network_client.update_router(
+        self.os_admin.network_client.update_router(
             router_id=router['id'], admin_state_up=True)
         self._check_connectivity()
 
diff --git a/neutron/tests/tempest/scenario/test_qos.py b/neutron/tests/tempest/scenario/test_qos.py
index c4d22cd..97f8ce1 100644
--- a/neutron/tests/tempest/scenario/test_qos.py
+++ b/neutron/tests/tempest/scenario/test_qos.py
@@ -20,7 +20,6 @@
 from tempest.lib import decorators
 from tempest.lib import exceptions
 from tempest import test
-import testtools
 
 from neutron.common import utils
 from neutron.services.qos import qos_consts
@@ -83,7 +82,6 @@
     @classmethod
     @test.requires_ext(extension="qos", service="network")
     @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
-    @testtools.skip('bug/1662109')
     def resource_setup(cls):
         super(QoSTest, cls).resource_setup()
 
@@ -155,19 +153,19 @@
         ssh_client = ssh.Client(self.fip['floating_ip_address'],
                                 CONF.validation.image_ssh_user,
                                 pkey=self.keypair['private_key'])
-        policy = self.admin_manager.network_client.create_qos_policy(
+        policy = self.os_admin.network_client.create_qos_policy(
                                         name='test-policy',
                                         description='test-qos-policy',
                                         shared=True)
         policy_id = policy['policy']['id']
-        self.admin_manager.network_client.create_bandwidth_limit_rule(
+        self.os_admin.network_client.create_bandwidth_limit_rule(
             policy_id, max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
             max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)
         port = self.client.list_ports(network_id=self.network['id'],
                                       device_id=self.server[
                                       'server']['id'])['ports'][0]
-        self.admin_manager.network_client.update_port(port['id'],
-                                                      qos_policy_id=policy_id)
+        self.os_admin.network_client.update_port(port['id'],
+                                                 qos_policy_id=policy_id)
         self._create_file_for_bw_tests(ssh_client)
         utils.wait_until_true(lambda: self._check_bw(
             ssh_client,
diff --git a/neutron/tests/tempest/scenario/test_trunk.py b/neutron/tests/tempest/scenario/test_trunk.py
index c857f3f..5d775ef 100644
--- a/neutron/tests/tempest/scenario/test_trunk.py
+++ b/neutron/tests/tempest/scenario/test_trunk.py
@@ -55,7 +55,7 @@
         router = cls.create_router_by_client()
         cls.create_router_interface(router['id'], cls.subnet['id'])
         cls.keypair = cls.create_keypair()
-        cls.secgroup = cls.manager.network_client.create_security_group(
+        cls.secgroup = cls.os_primary.network_client.create_security_group(
             name=data_utils.rand_name('secgroup-'))
         cls.security_groups.append(cls.secgroup['security_group'])
         cls.create_loginable_secgroup_rule(
@@ -86,7 +86,7 @@
     def _detach_and_delete_trunk(self, server, trunk):
         # we have to detach the interface from the server before
         # the trunk can be deleted.
-        self.manager.compute.InterfacesClient().delete_interface(
+        self.os_primary.compute.InterfacesClient().delete_interface(
             server['id'], trunk['port_id'])
 
         def is_port_detached():
@@ -137,7 +137,7 @@
         }
 
     def _wait_for_server(self, server):
-        waiters.wait_for_server_status(self.manager.servers_client,
+        waiters.wait_for_server_status(self.os_primary.servers_client,
                                        server['server']['id'],
                                        constants.SERVER_STATUS_ACTIVE)
         self.check_connectivity(server['fip']['floating_ip_address'],
diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py
index f5621ea..7a05da1 100644
--- a/neutron/tests/tempest/services/network/json/network_client.py
+++ b/neutron/tests/tempest/services/network/json/network_client.py
@@ -148,10 +148,11 @@
 
     def _updater(self, resource_name):
         def _update(res_id, **kwargs):
+            headers = kwargs.pop('headers', {})
             plural = self.pluralize(resource_name)
             uri = '%s/%s' % (self.get_uri(plural), res_id)
             post_data = self.serialize({resource_name: kwargs})
-            resp, body = self.put(uri, post_data)
+            resp, body = self.put(uri, post_data, headers=headers)
             body = self.deserialize_single(body)
             self.expected_success(200, resp.status)
             return service_client.ResponseBody(resp, body)