Merge "tests: don't set description in test_blank_update_clears_association"
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 9e12179..e0bf179 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
@@ -15,7 +15,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
 from neutron.tests.tempest.api import base_security_groups as base_security
@@ -26,7 +25,7 @@
 
     required_extensions = ['port-security']
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('d39a96e2-2dea-4feb-8093-e7ac991ce6f8')
     def test_create_port_security_false_on_shared_network(self):
         network = self.create_shared_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 e997380..e6184b3 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
@@ -16,7 +16,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
@@ -42,7 +41,7 @@
         cls.create_router_interface(cls.router['id'], cls.subnet['id'])
         cls.port = cls.create_port(cls.network)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('11116ee9-4e99-5b15-b8e1-aa7df92ca589')
     def test_associate_floating_ip_with_port_from_another_tenant(self):
         if not CONF.identity_feature_enabled.api_v2_admin:
diff --git a/neutron/tests/tempest/api/admin/test_quotas.py b/neutron/tests/tempest/api/admin/test_quotas.py
index 8091788..fe8f511 100644
--- a/neutron/tests/tempest/api/admin/test_quotas.py
+++ b/neutron/tests/tempest/api/admin/test_quotas.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
@@ -30,21 +31,18 @@
 
     @classmethod
     def resource_setup(cls):
-        if not CONF.identity_feature_enabled.api_v2_admin:
-            # TODO(ihrachys) adopt to v3
-            raise cls.skipException('Identity v2 admin not available')
         super(QuotasTestBase, cls).resource_setup()
 
     def _create_tenant(self):
         # Add a tenant to conduct the test
         test_tenant = data_utils.rand_name('test_tenant_')
         test_description = data_utils.rand_name('desc_')
-        tenant = self.identity_admin_client.create_tenant(
+        project = self.identity_admin_clientv3.create_project(
             name=test_tenant,
-            description=test_description)['tenant']
+            description=test_description)['project']
         self.addCleanup(
-            self.identity_admin_client.delete_tenant, tenant['id'])
-        return tenant
+            self.identity_admin_clientv3.delete_project, project['id'])
+        return project
 
     def _setup_quotas(self, project_id, **new_quotas):
         # Change quotas for tenant
@@ -62,6 +60,19 @@
         except lib_exc.NotFound:
             pass
 
+    def _create_network(self, project_id):
+        network = self.create_network(client=self.admin_client,
+                                      tenant_id=project_id)
+        self.addCleanup(self.admin_client.delete_network,
+                        network['id'])
+        return network
+
+    def _create_port(self, **kwargs):
+        port = self.admin_client.create_port(**kwargs)['port']
+        self.addCleanup(self.admin_client.delete_port,
+                        port['id'])
+        return port
+
 
 class QuotasTest(QuotasTestBase):
     """Test the Neutron API of Quotas.
@@ -71,6 +82,7 @@
 
         list quotas for tenants who have non-default quota values
         show quotas for a specified tenant
+        show detail quotas for a specified tenant
         update quotas for a specified tenant
         reset quotas to default values for a specified tenant
 
@@ -81,7 +93,7 @@
         quota_driver = neutron.db.driver.DbQuotaDriver
     """
 
-    @test.attr(type='gate')
+    @decorators.attr(type='gate')
     @decorators.idempotent_id('2390f766-836d-40ef-9aeb-e810d78207fb')
     def test_quotas(self):
         tenant_id = self._create_tenant()['id']
@@ -112,3 +124,39 @@
         non_default_quotas = self.admin_client.list_quotas()
         for q in non_default_quotas['quotas']:
             self.assertNotEqual(tenant_id, q['tenant_id'])
+
+    @decorators.idempotent_id('e974b5ba-090a-452c-a578-f9710151d9fc')
+    @decorators.attr(type='gate')
+    @test.requires_ext(extension="quota_details", service="network")
+    def test_detail_quotas(self):
+        tenant_id = self._create_tenant()['id']
+        new_quotas = {'network': {'used': 1, 'limit': 2, 'reserved': 0},
+                      'port': {'used': 1, 'limit': 2, 'reserved': 0}}
+
+        # update quota limit for tenant
+        new_quota = {'network': new_quotas['network']['limit'], 'port':
+                     new_quotas['port']['limit']}
+        quota_set = self._setup_quotas(tenant_id, **new_quota)
+
+        # create test resources
+        network = self._create_network(tenant_id)
+        post_body = {"network_id": network['id'],
+                     "tenant_id": tenant_id}
+        self._create_port(**post_body)
+
+        # confirm from extended API quotas were changed
+        # as requested for tenant
+        quota_set = self.admin_client.show_details_quota(tenant_id)
+        quota_set = quota_set['quota']
+        for key, value in six.iteritems(new_quotas):
+            self.assertEqual(new_quotas[key]['limit'],
+                             quota_set[key]['limit'])
+            self.assertEqual(new_quotas[key]['reserved'],
+                             quota_set[key]['reserved'])
+            self.assertEqual(new_quotas[key]['used'],
+                             quota_set[key]['used'])
+
+        # validate 'default' action for old extension
+        quota_limit = self.admin_client.show_quotas(tenant_id)['quota']
+        for key, value in six.iteritems(new_quotas):
+            self.assertEqual(new_quotas[key]['limit'], quota_limit[key])
diff --git a/neutron/tests/tempest/api/admin/test_quotas_negative.py b/neutron/tests/tempest/api/admin/test_quotas_negative.py
index 3887bea..84cd499 100644
--- a/neutron/tests/tempest/api/admin/test_quotas_negative.py
+++ b/neutron/tests/tempest/api/admin/test_quotas_negative.py
@@ -23,7 +23,7 @@
 
 class QuotasAdminNegativeTestJSON(test_quotas.QuotasTestBase):
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('952f9b24-9156-4bdc-90f3-682a3d4302f0')
     def test_create_network_when_quotas_is_full(self):
         tenant_id = self._create_tenant()['id']
@@ -37,7 +37,7 @@
         self.assertRaises(lib_exc.Conflict,
                           self.admin_client.create_network, **net_args)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('0b7f99e3-9f77-45ce-9a89-b39a184de618')
     def test_create_subnet_when_quotas_is_full(self):
         tenant_id = self._create_tenant()['id']
@@ -59,7 +59,7 @@
         self.assertRaises(lib_exc.Conflict,
                           self.admin_client.create_subnet, **subnet_args)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('fe20d9f9-346c-4a20-bbfa-d9ca390f4dc6')
     def test_create_port_when_quotas_is_full(self):
         tenant_id = self._create_tenant()['id']
@@ -86,7 +86,7 @@
         self.assertRaises(lib_exc.Conflict,
                           self.admin_client.create_port, **port_args)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('bb1e9c3c-7e6f-41f1-b579-63dbc655ecb7')
     @test.requires_ext(extension="router", service="network")
     def test_create_router_when_quotas_is_full(self):
@@ -104,7 +104,7 @@
                           self.admin_client.create_router,
                           name, True, **router_args)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('5c924ff7-b7a9-474f-92a3-dbe0f976ec13')
     @test.requires_ext(extension="security-group", service="network")
     def test_create_security_group_when_quotas_is_full(self):
@@ -125,7 +125,7 @@
         self.assertRaises(lib_exc.Conflict,
                           self.admin_client.create_security_group, **sg_args)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('b7143480-6118-4ed4-be38-1b6f15f30d05')
     @test.requires_ext(extension="security-group", service="network")
     def test_create_security_group_rule_when_quotas_is_full(self):
@@ -157,7 +157,7 @@
                           self.admin_client.create_security_group_rule,
                           **sg_rule_args)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('d00fe5bb-9db8-4e1a-9c31-490f52897e6f')
     @test.requires_ext(extension="router", service="network")
     def test_create_floatingip_when_quotas_is_full(self):
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 458a6f1..2be0ea2 100644
--- a/neutron/tests/tempest/api/admin/test_shared_network_extension.py
+++ b/neutron/tests/tempest/api/admin/test_shared_network_extension.py
@@ -65,19 +65,22 @@
                                     client=self.admin_client)
         self.assertIn(shared['id'],
                       self._list_subnets_ids(self.client, shared=True))
-        self.assertIn(shared,
-            self.admin_client.list_subnets(shared=True)['subnets'])
-        self.assertNotIn(priv,
-            self.client.list_subnets(shared=True)['subnets'])
-        self.assertNotIn(priv,
-            self.admin_client.list_subnets(shared=True)['subnets'])
-        self.assertIn(priv, self.client.list_subnets(shared=False)['subnets'])
+        self.assertIn(shared['id'],
+                      self._list_subnets_ids(self.admin_client, shared=True))
+        self.assertNotIn(priv['id'],
+                         self._list_subnets_ids(self.client, shared=True))
+        self.assertNotIn(
+            priv['id'],
+            self._list_subnets_ids(self.admin_client, shared=True))
+        self.assertIn(priv['id'],
+                      self._list_subnets_ids(self.client, shared=False))
         self.assertIn(priv['id'],
                       self._list_subnets_ids(self.admin_client, shared=False))
-        self.assertNotIn(shared,
-            self.client.list_subnets(shared=False)['subnets'])
-        self.assertNotIn(shared,
-            self.admin_client.list_subnets(shared=False)['subnets'])
+        self.assertNotIn(shared['id'],
+                         self._list_subnets_ids(self.client, shared=False))
+        self.assertNotIn(
+            shared['id'],
+            self._list_subnets_ids(self.admin_client, shared=False))
 
     @decorators.idempotent_id('6661d219-b96d-4597-ad10-55766ce4abf7')
     def test_create_update_shared_network(self):
@@ -193,14 +196,14 @@
         )['rbac_policy']
         return {'network': net, 'subnet': subnet, 'policy': pol}
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('86c3529b-1231-40de-803c-bfffffff1eee')
     def test_create_rbac_policy_with_target_tenant_none(self):
         with testtools.ExpectedException(lib_exc.BadRequest):
             self._make_admin_net_and_subnet_shared_to_tenant_id(
                 tenant_id=None)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('86c3529b-1231-40de-803c-bfffffff1fff')
     def test_create_rbac_policy_with_target_tenant_too_long_id(self):
         with testtools.ExpectedException(lib_exc.BadRequest):
@@ -208,7 +211,7 @@
             self._make_admin_net_and_subnet_shared_to_tenant_id(
                 tenant_id=target_tenant)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('86c3529b-1231-40de-803c-afffffff1fff')
     def test_network_only_visible_to_policy_target(self):
         net = self._make_admin_net_and_subnet_shared_to_tenant_id(
@@ -423,6 +426,21 @@
                                        target_tenant=self.client2.tenant_id)
         self.client.delete_port(port['id'])
 
+    @test.requires_ext(extension="standard-attr-revisions", service="network")
+    @decorators.idempotent_id('86c3529b-1231-40de-1234-89664291a4cb')
+    def test_rbac_bumps_network_revision(self):
+        resp = self._make_admin_net_and_subnet_shared_to_tenant_id(
+            self.client.tenant_id)
+        net_id = resp['network']['id']
+        rev = self.client.show_network(net_id)['network']['revision_number']
+        self.admin_client.create_rbac_policy(
+            object_type='network', object_id=net_id,
+            action='access_as_shared', target_tenant='*')
+        self.assertGreater(
+            self.client.show_network(net_id)['network']['revision_number'],
+            rev
+        )
+
     @decorators.idempotent_id('86c3529b-1231-40de-803c-aeeeeeee7fff')
     def test_filtering_works_with_rbac_records_present(self):
         resp = self._make_admin_net_and_subnet_shared_to_tenant_id(
diff --git a/neutron/tests/tempest/api/base.py b/neutron/tests/tempest/api/base.py
index d0b8382..e735502 100644
--- a/neutron/tests/tempest/api/base.py
+++ b/neutron/tests/tempest/api/base.py
@@ -90,7 +90,7 @@
     @classmethod
     def setup_clients(cls):
         super(BaseNetworkTest, cls).setup_clients()
-        cls.client = cls.os.network_client
+        cls.client = cls.os_primary.network_client
 
     @classmethod
     def resource_setup(cls):
@@ -217,11 +217,12 @@
             pass
 
     @classmethod
-    def create_network(cls, network_name=None, **kwargs):
+    def create_network(cls, network_name=None, client=None, **kwargs):
         """Wrapper utility that returns a test network."""
         network_name = network_name or data_utils.rand_name('test-network-')
 
-        body = cls.client.create_network(name=network_name, **kwargs)
+        client = client or cls.client
+        body = client.create_network(name=network_name, **kwargs)
         network = body['network']
         cls.networks.append(network)
         return network
@@ -428,8 +429,9 @@
     @classmethod
     def setup_clients(cls):
         super(BaseAdminNetworkTest, cls).setup_clients()
-        cls.admin_client = cls.os_adm.network_client
-        cls.identity_admin_client = cls.os_adm.tenants_client
+        cls.admin_client = cls.os_admin.network_client
+        cls.identity_admin_client = cls.os_admin.tenants_client
+        cls.identity_admin_clientv3 = cls.os_admin.projects_client
 
     @classmethod
     def create_metering_label(cls, name, description):
diff --git a/neutron/tests/tempest/api/clients.py b/neutron/tests/tempest/api/clients.py
index c6f41d0..949ce2e 100644
--- a/neutron/tests/tempest/api/clients.py
+++ b/neutron/tests/tempest/api/clients.py
@@ -16,6 +16,7 @@
 from tempest.lib.services.compute import keypairs_client
 from tempest.lib.services.compute import servers_client
 from tempest.lib.services.identity.v2 import tenants_client
+from tempest.lib.services.identity.v3 import projects_client
 from tempest import manager
 
 from neutron.tests.tempest import config
@@ -85,3 +86,6 @@
         # Client uses admin endpoint type of Keystone API v2
         self.tenants_client = tenants_client.TenantsClient(self.auth_provider,
                                                            **params_v2_admin)
+        # Client uses admin endpoint type of Keystone API v3
+        self.projects_client = projects_client.ProjectsClient(
+            self.auth_provider, **params_v2_admin)
diff --git a/neutron/tests/tempest/api/test_address_scopes_negative.py b/neutron/tests/tempest/api/test_address_scopes_negative.py
index ac8956d..2e95f59 100644
--- a/neutron/tests/tempest/api/test_address_scopes_negative.py
+++ b/neutron/tests/tempest/api/test_address_scopes_negative.py
@@ -15,39 +15,38 @@
 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
 
 from neutron.tests.tempest.api import test_address_scopes
 
 
 class AddressScopeTestNegative(test_address_scopes.AddressScopeTestBase):
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('9c92ec34-0c50-4104-aa47-9ce98d5088df')
     def test_tenant_create_shared_address_scope(self):
         self.assertRaises(lib_exc.Forbidden, self._create_address_scope,
                           shared=True, ip_version=4)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('a857b61e-bf53-4fab-b21a-b0daaf81b5bd')
     def test_tenant_update_address_scope_shared_true(self):
         self.assertRaises(lib_exc.Forbidden,
                           self._test_update_address_scope_helper, shared=True)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('a859ef2f-9c76-4e2e-ba0f-e0339a489e8c')
     def test_tenant_update_address_scope_shared_false(self):
         self.assertRaises(lib_exc.Forbidden,
                           self._test_update_address_scope_helper, shared=False)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('9b6dd7ad-cabb-4f55-bd5e-e61176ef41f6')
     def test_get_non_existent_address_scope(self):
         non_exist_id = data_utils.rand_name('address_scope')
         self.assertRaises(lib_exc.NotFound, self.client.show_address_scope,
                           non_exist_id)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('ef213552-f2da-487d-bf4a-e1705d115ff1')
     def test_tenant_get_not_shared_admin_address_scope(self):
         address_scope = self._create_address_scope(is_admin=True,
@@ -56,21 +55,21 @@
         self.assertRaises(lib_exc.NotFound, self.client.show_address_scope,
                           address_scope['id'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('5c25dc6a-1e92-467a-9cc7-cda74b6003db')
     def test_delete_non_existent_address_scope(self):
         non_exist_id = data_utils.rand_name('address_scope')
         self.assertRaises(lib_exc.NotFound, self.client.delete_address_scope,
                           non_exist_id)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('47c25dc5-e886-4a84-88c3-ac5031969661')
     def test_update_non_existent_address_scope(self):
         non_exist_id = data_utils.rand_name('address_scope')
         self.assertRaises(lib_exc.NotFound, self.client.update_address_scope,
                           non_exist_id, name='foo-name')
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('702d0515-82cb-4207-b0d9-703336e54665')
     def test_update_shared_address_scope_to_unshare(self):
         address_scope = self._create_address_scope(is_admin=True, shared=True,
@@ -79,7 +78,7 @@
                           self.admin_client.update_address_scope,
                           address_scope['id'], name='new-name', shared=False)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('1e471e5c-6f9c-437a-9257-fd9bc4b6f0fb')
     def test_delete_address_scope_associated_with_subnetpool(self):
         address_scope = self._create_address_scope(ip_version=4)
diff --git a/neutron/tests/tempest/api/test_auto_allocated_topology.py b/neutron/tests/tempest/api/test_auto_allocated_topology.py
index 0f0de54..e7ebfa0 100644
--- a/neutron/tests/tempest/api/test_auto_allocated_topology.py
+++ b/neutron/tests/tempest/api/test_auto_allocated_topology.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from neutron_lib.api.definitions import auto_allocated_topology
 from oslo_config import cfg
 from tempest.lib import decorators
 
@@ -30,7 +31,7 @@
     # all tests are added under TestAutoAllocatedTopology,
     # nothing bad should happen.
     force_tenant_isolation = True
-    required_extensions = ['auto-allocated-topology']
+    required_extensions = [auto_allocated_topology.ALIAS]
 
     @classmethod
     def resource_setup(cls):
@@ -84,7 +85,7 @@
         self.assertEqual((0, 0, 0), resources_before)
 
         body = self.client.get_auto_allocated_topology()
-        topology = body['auto_allocated_topology']
+        topology = body[auto_allocated_topology.RESOURCE_NAME]
         self.assertIsNotNone(topology)
         self._add_topology_cleanup(self.client)
 
@@ -97,7 +98,7 @@
         self.assertEqual((1, self.num_subnetpools, 1), resources_after1)
 
         body = self.client.get_auto_allocated_topology()
-        topology = body['auto_allocated_topology']
+        topology = body[auto_allocated_topology.RESOURCE_NAME]
         network_id2 = topology['id']
         resources_after2 = self._count_topology_resources()
         # After the initial GET, the API should be idempotent
@@ -109,7 +110,7 @@
         resources_before = self._count_topology_resources()
         self.assertEqual((0, 0, 0), resources_before)
         body = self.client.get_auto_allocated_topology()
-        topology = body['auto_allocated_topology']
+        topology = body[auto_allocated_topology.RESOURCE_NAME]
         self.assertIsNotNone(topology)
         self.client.delete_auto_allocated_topology()
         resources_after = self._count_topology_resources()
diff --git a/neutron/tests/tempest/api/test_extension_driver_port_security.py b/neutron/tests/tempest/api/test_extension_driver_port_security.py
index 381252a..bd7d801 100644
--- a/neutron/tests/tempest/api/test_extension_driver_port_security.py
+++ b/neutron/tests/tempest/api/test_extension_driver_port_security.py
@@ -69,7 +69,7 @@
         self.assertFalse(port['port_security_enabled'])
         self.assertEmpty(port['security_groups'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('ff11226c-a5ff-4ad4-8480-0840e36e47a9')
     @test.requires_ext(extension='port-security', service='network')
     def test_port_sec_update_port_failed(self):
@@ -133,7 +133,7 @@
         self.assertTrue(
             self.client.is_resource_deleted('network', network['id']))
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('ed93e453-3f8d-495e-8e7e-b0e268c2ebd9')
     @test.requires_ext(extension='port-security', service='network')
     @test.requires_ext(extension='allowed-address-pairs', service='network')
diff --git a/neutron/tests/tempest/api/test_floating_ips_negative.py b/neutron/tests/tempest/api/test_floating_ips_negative.py
index 53e1523..84cd2ec 100644
--- a/neutron/tests/tempest/api/test_floating_ips_negative.py
+++ b/neutron/tests/tempest/api/test_floating_ips_negative.py
@@ -17,7 +17,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
 
 from neutron.tests.tempest.api import base
 from neutron.tests.tempest import config
@@ -40,7 +39,7 @@
         cls.create_router_interface(cls.router['id'], cls.subnet['id'])
         cls.port = cls.create_port(cls.network)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('0b5b8797-6de7-4191-905c-a48b888eb429')
     def test_associate_floatingip_with_port_with_floatingip(self):
         net = self.create_network()
diff --git a/neutron/tests/tempest/api/test_metering_negative.py b/neutron/tests/tempest/api/test_metering_negative.py
index cbdf74d..4b70523 100644
--- a/neutron/tests/tempest/api/test_metering_negative.py
+++ b/neutron/tests/tempest/api/test_metering_negative.py
@@ -15,7 +15,6 @@
 from neutron_lib.db import constants as db_const
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 from neutron.tests.tempest.api import base
 
@@ -26,7 +25,7 @@
 
     required_extensions = ['metering']
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('8b3f7c84-9d37-4771-8681-bfd2c07f3c2d')
     def test_create_metering_label_with_too_long_name(self):
         self.assertRaises(lib_exc.BadRequest,
diff --git a/neutron/tests/tempest/api/test_network_ip_availability_negative.py b/neutron/tests/tempest/api/test_network_ip_availability_negative.py
index bb95ed0..7e9b42b 100644
--- a/neutron/tests/tempest/api/test_network_ip_availability_negative.py
+++ b/neutron/tests/tempest/api/test_network_ip_availability_negative.py
@@ -15,14 +15,13 @@
 from oslo_utils import uuidutils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 from neutron.tests.tempest.api import test_network_ip_availability as net_ip
 
 
 class NetworksIpAvailabilityNegativeTest(net_ip.NetworksIpAvailabilityTest):
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('3b8693eb-6c57-4ea1-ab84-3730c9ee9c84')
     def test_network_availability_nonexistent_network_id(self):
         self.assertRaises(lib_exc.NotFound,
diff --git a/neutron/tests/tempest/api/test_networks.py b/neutron/tests/tempest/api/test_networks.py
index 0b39569..b350068 100644
--- a/neutron/tests/tempest/api/test_networks.py
+++ b/neutron/tests/tempest/api/test_networks.py
@@ -15,8 +15,10 @@
 
 from tempest.lib import decorators
 from tempest import test
+import testtools
 
 from neutron.tests.tempest.api import base
+from neutron.tests.tempest import config
 
 
 class NetworksTestJSON(base.BaseNetworkTest):
@@ -129,6 +131,35 @@
         _check_list_networks_fields(['project_id', 'tenant_id'], True, True)
 
 
+# TODO(ihrachys): check that bad mtu is not allowed; current API extension
+# definition doesn't enforce values
+# TODO(ihrachys): check that new segment reservation updates mtu, once
+# https://review.openstack.org/#/c/353115/ is merged
+class NetworksMtuTestJSON(base.BaseNetworkTest):
+    required_extensions = ['net-mtu', 'net-mtu-writable']
+
+    @decorators.idempotent_id('c79dbf94-ee26-420f-a56f-382aaccb1a41')
+    def test_create_network_custom_mtu(self):
+        # 68 should be supported by all implementations, as per api-ref
+        network = self.create_network(mtu=68)
+        body = self.client.show_network(network['id'])['network']
+        self.assertEqual(68, body['mtu'])
+
+    @decorators.idempotent_id('2d35d49d-9d16-465c-92c7-4768eb717688')
+    @testtools.skipUnless(config.CONF.network_feature_enabled.ipv6,
+                          'IPv6 is not enabled')
+    def test_update_network_custom_mtu(self):
+        # 68 should be supported by all implementations, as per api-ref
+        network = self.create_network(mtu=68)
+        body = self.client.show_network(network['id'])['network']
+        self.assertEqual(68, body['mtu'])
+
+        # 1280 should be supported by all ipv6 compliant implementations
+        self.client.update_network(network['id'], mtu=1280)
+        body = self.client.show_network(network['id'])['network']
+        self.assertEqual(1280, body['mtu'])
+
+
 class NetworksSearchCriteriaTest(base.BaseSearchCriteriaTest):
 
     resource = 'network'
diff --git a/neutron/tests/tempest/api/test_networks_negative.py b/neutron/tests/tempest/api/test_networks_negative.py
index 249ef5f..3aae948 100644
--- a/neutron/tests/tempest/api/test_networks_negative.py
+++ b/neutron/tests/tempest/api/test_networks_negative.py
@@ -12,7 +12,6 @@
 
 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
@@ -26,7 +25,7 @@
         cls.network = cls.create_network()
         cls.subnet = cls.create_subnet(cls.network)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('9f80f25b-5d1b-4f26-9f6b-774b9b270819')
     def test_delete_network_in_use(self):
         port = self.client.create_port(network_id=self.network['id'])
diff --git a/neutron/tests/tempest/api/test_ports.py b/neutron/tests/tempest/api/test_ports.py
index 8d8468c..acafa57 100644
--- a/neutron/tests/tempest/api/test_ports.py
+++ b/neutron/tests/tempest/api/test_ports.py
@@ -91,6 +91,30 @@
         self.assertFalse(port_body['port']['dns_name'])
         self._confirm_dns_assignment(port_body['port'])
 
+    @decorators.idempotent_id('dfe8cc79-18d9-4ae8-acef-3ec6bb719aa7')
+    @test.requires_ext(extension="dns-domain-ports",
+                       service="network")
+    def test_create_update_port_with_dns_domain(self):
+        self.create_subnet(self.network)
+        body = self.create_port(self.network, dns_name='d1',
+                                dns_domain='test.org.')
+        self.assertEqual('d1', body['dns_name'])
+        self.assertEqual('test.org.', body['dns_domain'])
+        self._confirm_dns_assignment(body)
+        body = self.client.list_ports(id=body['id'])['ports'][0]
+        self._confirm_dns_assignment(body)
+        self.assertEqual('d1', body['dns_name'])
+        self.assertEqual('test.org.', body['dns_domain'])
+        body = self.client.update_port(body['id'],
+                                       dns_name='d2', dns_domain='d.org.')
+        self.assertEqual('d2', body['port']['dns_name'])
+        self.assertEqual('d.org.', body['dns_domain'])
+        self._confirm_dns_assignment(body['port'])
+        body = self.client.show_port(body['port']['id'])['port']
+        self.assertEqual('d2', body['dns_name'])
+        self.assertEqual('d.org.', body['dns_domain'])
+        self._confirm_dns_assignment(body)
+
     @decorators.idempotent_id('c72c1c0c-2193-4aca-bbb4-b1442640c123')
     def test_change_dhcp_flag_then_create_port(self):
         s = self.create_subnet(self.network, enable_dhcp=False)
diff --git a/neutron/tests/tempest/api/test_qos_negative.py b/neutron/tests/tempest/api/test_qos_negative.py
index 3bc6088..275fe05 100644
--- a/neutron/tests/tempest/api/test_qos_negative.py
+++ b/neutron/tests/tempest/api/test_qos_negative.py
@@ -13,7 +13,6 @@
 from neutron_lib.db import constants as db_const
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 from neutron.tests.tempest.api import base
 
@@ -26,21 +25,21 @@
 
     required_extensions = ['qos']
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('b9dce555-d3b3-11e5-950a-54ee757c77da')
     def test_add_policy_with_too_long_name(self):
         self.assertRaises(lib_exc.BadRequest,
                           self.client.create_qos_policy,
                           LONG_NAME_NG, 'test policy desc1', False)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('b9dce444-d3b3-11e5-950a-54ee747c99db')
     def test_add_policy_with_too_long_description(self):
         self.assertRaises(lib_exc.BadRequest,
                           self.client.create_qos_policy,
                           'test-policy', LONG_DESCRIPTION_NG, False)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('b9dce444-d3b3-11e5-950a-54ee757c77dc')
     def test_add_policy_with_too_long_tenant_id(self):
         self.assertRaises(lib_exc.BadRequest,
diff --git a/neutron/tests/tempest/api/test_routers_negative.py b/neutron/tests/tempest/api/test_routers_negative.py
index 21b5a25..b2b198d 100644
--- a/neutron/tests/tempest/api/test_routers_negative.py
+++ b/neutron/tests/tempest/api/test_routers_negative.py
@@ -16,7 +16,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_routers as base
@@ -36,7 +35,7 @@
 
 class RoutersNegativeTest(RoutersNegativeTestBase):
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('e3e751af-15a2-49cc-b214-a7154579e94f')
     def test_delete_router_in_use(self):
         # This port is deleted after a test by remove_router_interface.
@@ -51,7 +50,7 @@
 
     credentials = ['admin', 'primary', 'alt']
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('159f576d-a423-46b5-b501-622694c02f6b')
     def test_add_interface_wrong_tenant(self):
         client2 = self.os_alt.network_client
@@ -73,7 +72,7 @@
 
     required_extensions = ['dvr']
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('4990b055-8fc7-48ab-bba7-aa28beaad0b9')
     def test_router_create_tenant_distributed_returns_forbidden(self):
         with testtools.ExpectedException(lib_exc.Forbidden):
@@ -85,7 +84,7 @@
 
     required_extensions = ['l3-ha']
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('821b85b9-9c51-40f3-831f-bf223a7e0084')
     def test_router_create_tenant_ha_returns_forbidden(self):
         with testtools.ExpectedException(lib_exc.Forbidden):
diff --git a/neutron/tests/tempest/api/test_security_groups_negative.py b/neutron/tests/tempest/api/test_security_groups_negative.py
index c68e0b9..649f375 100644
--- a/neutron/tests/tempest/api/test_security_groups_negative.py
+++ b/neutron/tests/tempest/api/test_security_groups_negative.py
@@ -16,7 +16,6 @@
 from neutron_lib.db import constants as db_const
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 from neutron.tests.tempest.api import base_security_groups as base
 
@@ -27,21 +26,21 @@
 
     required_extensions = ['security-group']
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('594edfa8-9a5b-438e-9344-49aece337d49')
     def test_create_security_group_with_too_long_name(self):
         self.assertRaises(lib_exc.BadRequest,
                           self.client.create_security_group,
                           name=LONG_NAME_NG)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('b6b79838-7430-4d3f-8e07-51dfb61802c2')
     def test_create_security_group_with_boolean_type_name(self):
         self.assertRaises(lib_exc.BadRequest,
                           self.client.create_security_group,
                           name=True)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('55100aa8-b24f-333c-0bef-64eefd85f15c')
     def test_update_default_security_group_name(self):
         sg_list = self.client.list_security_groups(name='default')
@@ -49,7 +48,7 @@
         self.assertRaises(lib_exc.Conflict, self.client.update_security_group,
                           sg['id'], name='test')
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('c8510dd8-c3a8-4df9-ae44-24354db50960')
     def test_update_security_group_with_too_long_name(self):
         sg_list = self.client.list_security_groups(name='default')
@@ -58,7 +57,7 @@
                           self.client.update_security_group,
                           sg['id'], name=LONG_NAME_NG)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('d9a14917-f66f-4eca-ab72-018563917f1b')
     def test_update_security_group_with_boolean_type_name(self):
         sg_list = self.client.list_security_groups(name='default')
diff --git a/neutron/tests/tempest/api/test_subnetpools.py b/neutron/tests/tempest/api/test_subnetpools.py
index 1132d54..a2d4fe8 100644
--- a/neutron/tests/tempest/api/test_subnetpools.py
+++ b/neutron/tests/tempest/api/test_subnetpools.py
@@ -389,7 +389,7 @@
         subnetpool_data = {'is_default': False}
         self.admin_client.update_subnetpool(subnetpool_id,
                                             **subnetpool_data)
-        show_body = self.client.show_subnetpool(subnetpool_id)
+        show_body = self.admin_client.show_subnetpool(subnetpool_id)
         self.assertFalse(show_body['subnetpool']['is_default'])
 
 
diff --git a/neutron/tests/tempest/api/test_subnetpools_negative.py b/neutron/tests/tempest/api/test_subnetpools_negative.py
index 017eca4..5467664 100644
--- a/neutron/tests/tempest/api/test_subnetpools_negative.py
+++ b/neutron/tests/tempest/api/test_subnetpools_negative.py
@@ -30,14 +30,14 @@
 
     smaller_prefix = u'10.11.12.0/26'
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('0212a042-603a-4f46-99e0-e37de9374d30')
     def test_get_non_existent_subnetpool(self):
         non_exist_id = data_utils.rand_name('subnetpool')
         self.assertRaises(lib_exc.NotFound, self.client.show_subnetpool,
                           non_exist_id)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('dc9336e5-f28f-4658-a0b0-cc79e607007d')
     def test_tenant_get_not_shared_admin_subnetpool(self):
         created_subnetpool = self._create_subnetpool(is_admin=True)
@@ -45,21 +45,21 @@
         self.assertRaises(lib_exc.NotFound, self.client.show_subnetpool,
                           created_subnetpool['id'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('5e1f2f86-d81a-498c-82ed-32a49f4dc4d3')
     def test_delete_non_existent_subnetpool(self):
         non_exist_id = data_utils.rand_name('subnetpool')
         self.assertRaises(lib_exc.NotFound, self.client.delete_subnetpool,
                           non_exist_id)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('d1143fe2-212b-4e23-a308-d18f7d8d78d6')
     def test_tenant_create_shared_subnetpool(self):
         # 'shared' subnetpool can only be created by admin.
         self.assertRaises(lib_exc.Forbidden, self._create_subnetpool,
                           is_admin=False, shared=True)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('6ae09d8f-95be-40ed-b1cf-8b850d45bab5')
     @test.requires_ext(extension='default-subnetpools', service='network')
     def test_tenant_create_default_subnetpool(self):
@@ -67,14 +67,14 @@
         self.assertRaises(lib_exc.Forbidden, self._create_subnetpool,
                           is_admin=False, is_default=True)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('4be84d30-60ca-4bd3-8512-db5b36ce1378')
     def test_update_non_existent_subnetpool(self):
         non_exist_id = data_utils.rand_name('subnetpool')
         self.assertRaises(lib_exc.NotFound, self.client.update_subnetpool,
                           non_exist_id, name='foo-name')
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('e6cd6d87-6173-45dd-bf04-c18ea7ec7537')
     def test_update_subnetpool_not_modifiable_shared(self):
         # 'shared' attributes can be specified during creation.
@@ -84,7 +84,7 @@
         self.assertRaises(lib_exc.BadRequest, self.client.update_subnetpool,
                           pool_id, shared=True)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('62f7c43b-bff1-4def-8bb7-4754b840aaad')
     def test_update_subnetpool_prefixes_shrink(self):
         # Shrink current subnetpool prefixes is not supported
@@ -94,7 +94,7 @@
                           created_subnetpool['id'],
                           prefixes=[self.smaller_prefix])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('fc011824-153e-4469-97ad-9808eb88cae1')
     def test_create_subnet_different_pools_same_network(self):
         network = self.create_network(network_name='smoke-network')
@@ -111,21 +111,21 @@
                           subnetpool_id=created_subnetpool['id'],
                           client=self.admin_client)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('9589e332-638e-476e-81bd-013d964aa3cb')
     @test.requires_ext(extension='address-scope', service='network')
     def test_create_subnetpool_associate_invalid_address_scope(self):
         self.assertRaises(lib_exc.BadRequest, self._create_subnetpool,
                           address_scope_id='foo-addr-scope')
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('3b6c5942-485d-4964-a560-55608af020b5')
     @test.requires_ext(extension='address-scope', service='network')
     def test_create_subnetpool_associate_non_exist_address_scope(self):
         self.assertRaises(lib_exc.NotFound, self._create_subnetpool,
                           address_scope_id=uuidutils.generate_uuid())
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('2dfb4269-8657-485a-a053-b022e911456e')
     @test.requires_ext(extension='address-scope', service='network')
     def test_create_subnetpool_associate_address_scope_prefix_intersect(self):
@@ -141,7 +141,7 @@
         self.assertRaises(lib_exc.Conflict, self._create_subnetpool,
                           **subnetpool_data)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('83a19a13-5384-42e2-b579-43fc69c80914')
     @test.requires_ext(extension='address-scope', service='network')
     def test_create_sp_associate_address_scope_multiple_prefix_intersect(self):
@@ -159,7 +159,7 @@
         self.assertRaises(lib_exc.Conflict, self._create_subnetpool,
                           **subnetpool_data)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('f06d8e7b-908b-4e94-b570-8156be6a4bf1')
     @test.requires_ext(extension='address-scope', service='network')
     def test_create_subnetpool_associate_address_scope_of_other_owner(self):
@@ -169,7 +169,7 @@
         self.assertRaises(lib_exc.NotFound, self._create_subnetpool,
                           address_scope_id=address_scope['id'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('3396ec6c-cb80-4ebe-b897-84e904580bdf')
     @test.requires_ext(extension='address-scope', service='network')
     def test_tenant_create_subnetpool_associate_shared_address_scope(self):
@@ -179,7 +179,7 @@
         self.assertRaises(lib_exc.BadRequest, self._create_subnetpool,
                           address_scope_id=address_scope['id'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('6d3d9ad5-32d4-4d63-aa00-8c62f73e2881')
     @test.requires_ext(extension='address-scope', service='network')
     def test_update_subnetpool_associate_address_scope_of_other_owner(self):
@@ -212,7 +212,7 @@
         self.assertRaises(lib_exc.Conflict, self.client.update_subnetpool,
                           pool_id_1, prefixes=pool_1_updated_prefixes)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('96006292-7214-40e0-a471-153fb76e6b31')
     @test.requires_ext(extension='address-scope', service='network')
     def test_update_subnetpool_prefix_intersect(self):
@@ -222,7 +222,7 @@
         self._test_update_subnetpool_prefix_intersect_helper(
             pool_1_prefix, pool_2_prefix, pool_1_updated_prefix)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('4d3f8a79-c530-4e59-9acf-6c05968adbfe')
     @test.requires_ext(extension='address-scope', service='network')
     def test_update_subnetpool_multiple_prefix_intersect(self):
@@ -233,7 +233,7 @@
         self._test_update_subnetpool_prefix_intersect_helper(
             pool_1_prefixes, pool_2_prefixes, pool_1_updated_prefixes)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('7438e49e-1351-45d8-937b-892059fb97f5')
     @test.requires_ext(extension='address-scope', service='network')
     def test_tenant_update_sp_prefix_associated_with_shared_addr_scope(self):
@@ -264,7 +264,7 @@
         self.assertEqual(update_prefixes,
                          body['subnetpool']['prefixes'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('648fee7d-a909-4ced-bad3-3a169444c0a8')
     @test.requires_ext(extension='address-scope', service='network')
     def test_update_subnetpool_associate_address_scope_wrong_ip_version(self):
@@ -276,7 +276,7 @@
                           created_subnetpool['id'],
                           address_scope_id=address_scope['id'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('2f66dc2f-cc32-4caa-91ec-0c0cd7c46d70')
     def test_update_subnetpool_tenant_id(self):
         subnetpool = self._create_subnetpool()
diff --git a/neutron/tests/tempest/api/test_tag.py b/neutron/tests/tempest/api/test_tag.py
index f819671..0819951 100644
--- a/neutron/tests/tempest/api/test_tag.py
+++ b/neutron/tests/tempest/api/test_tag.py
@@ -83,7 +83,7 @@
         network = cls.create_network()
         return network['id']
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('5621062d-fbfb-4437-9d69-138c78ea4188')
     def test_network_tags(self):
         self._test_tag_operations()
@@ -98,7 +98,7 @@
         subnet = cls.create_subnet(network)
         return subnet['id']
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('2805aabf-a94c-4e70-a0b2-9814f06beb03')
     @test.requires_ext(extension="tag-ext", service="network")
     def test_subnet_tags(self):
@@ -114,7 +114,7 @@
         port = cls.create_port(network)
         return port['id']
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('c7c44f2c-edb0-4ebd-a386-d37cec155c34')
     @test.requires_ext(extension="tag-ext", service="network")
     def test_port_tags(self):
@@ -130,7 +130,7 @@
                                            prefixes=['10.0.0.0/8'])
         return subnetpool['id']
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('bdc1c24b-c0b5-4835-953c-8f67dc11edfe')
     @test.requires_ext(extension="tag-ext", service="network")
     def test_subnetpool_tags(self):
@@ -145,7 +145,7 @@
         router = cls.create_router(router_name='test')
         return router['id']
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('b898ff92-dc33-4232-8ab9-2c6158c80d28')
     @test.requires_ext(extension="router", service="network")
     @test.requires_ext(extension="tag-ext", service="network")
@@ -237,7 +237,7 @@
         res = self.client.list_networks(**filters)
         return res[self.resource]
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('a66b5cca-7db2-40f5-a33d-8ac9f864e53e')
     def test_filter_network_tags(self):
         self._test_filter_tags()
@@ -256,7 +256,7 @@
         res = self.client.list_subnets(**filters)
         return res[self.resource]
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('dd8f9ba7-bcf6-496f-bead-714bd3daac10')
     @test.requires_ext(extension="tag-ext", service="network")
     def test_filter_subnet_tags(self):
@@ -276,7 +276,7 @@
         res = self.client.list_ports(**filters)
         return res[self.resource]
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('09c036b8-c8d0-4bee-b776-7f4601512898')
     @test.requires_ext(extension="tag-ext", service="network")
     def test_filter_port_tags(self):
@@ -296,7 +296,7 @@
         res = self.client.list_subnetpools(**filters)
         return res[self.resource]
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('16ae7ad2-55c2-4821-9195-bfd04ab245b7')
     @test.requires_ext(extension="tag-ext", service="network")
     def test_filter_subnetpool_tags(self):
@@ -315,7 +315,7 @@
         res = self.client.list_routers(**filters)
         return res[self.resource]
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('cdd3f3ea-073d-4435-a6cb-826a4064193d')
     @test.requires_ext(extension="tag-ext", service="network")
     def test_filter_router_tags(self):
@@ -332,7 +332,7 @@
         res_body = self.client.get_tags('networks', res_id)
         self.assertItemsEqual(tags, res_body['tags'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('74c56fb1-a3b1-4a62-a8d2-d04dca6bd4cd')
     def test_update_tags_affects_only_updated_resource(self):
         res1 = self.create_network()
diff --git a/neutron/tests/tempest/api/test_trunk_negative.py b/neutron/tests/tempest/api/test_trunk_negative.py
index f3497bb..b34b5fd 100644
--- a/neutron/tests/tempest/api/test_trunk_negative.py
+++ b/neutron/tests/tempest/api/test_trunk_negative.py
@@ -15,7 +15,6 @@
 from oslo_utils import uuidutils
 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 test_trunk
@@ -23,13 +22,13 @@
 
 class TrunkTestJSON(test_trunk.TrunkTestJSONBase):
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('1b5cf87a-1d3a-4a94-ba64-647153d54f32')
     def test_create_trunk_nonexistent_port_id(self):
         self.assertRaises(lib_exc.NotFound, self.client.create_trunk,
                           uuidutils.generate_uuid(), [])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('980bca3b-b0be-45ac-8067-b401e445b796')
     def test_create_trunk_nonexistent_subport_port_id(self):
         network = self.create_network()
@@ -40,7 +39,7 @@
                             'segmentation_type': 'vlan',
                             'segmentation_id': 2}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('a5c5200a-72a0-43c5-a11a-52f808490344')
     def test_create_subport_nonexistent_port_id(self):
         trunk = self._create_trunk_with_network_and_parent([])
@@ -50,7 +49,7 @@
                             'segmentation_type': 'vlan',
                             'segmentation_id': 2}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('80deb6a9-da2a-48db-b7fd-bcef5b14edc1')
     def test_create_subport_nonexistent_trunk(self):
         network = self.create_network()
@@ -61,7 +60,7 @@
                             'segmentation_type': 'vlan',
                             'segmentation_id': 2}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('7e0f99ab-fe37-408b-a889-9e44ef300084')
     def test_create_subport_missing_segmentation_id(self):
         trunk = self._create_trunk_with_network_and_parent([])
@@ -72,7 +71,7 @@
                           [{'port_id': parent_port['id'],
                             'segmentation_type': 'vlan'}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('a315d78b-2f43-4efa-89ae-166044c568aa')
     def test_create_trunk_with_subport_missing_segmentation_id(self):
         subport_network = self.create_network()
@@ -82,7 +81,7 @@
                           [{'port_id': uuidutils.generate_uuid(),
                             'segmentation_type': 'vlan'}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('33498618-f75a-4796-8ae6-93d4fd203fa4')
     def test_create_trunk_with_subport_missing_segmentation_type(self):
         subport_network = self.create_network()
@@ -92,7 +91,7 @@
                           [{'port_id': uuidutils.generate_uuid(),
                             'segmentation_id': 3}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('a717691c-4e07-4d81-a98d-6f1c18c5d183')
     def test_create_trunk_with_subport_missing_port_id(self):
         subport_network = self.create_network()
@@ -102,7 +101,7 @@
                           [{'segmentation_type': 'vlan',
                             'segmentation_id': 3}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('40aed9be-e976-47d0-dada-bde2c7e74e57')
     def test_create_subport_invalid_inherit_network_segmentation_type(self):
         trunk = self._create_trunk_with_network_and_parent([])
@@ -114,7 +113,7 @@
                             'segmentation_type': 'inherit',
                             'segmentation_id': -1}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('40aed9be-e976-47d0-a555-bde2c7e74e57')
     def test_create_trunk_duplicate_subport_segmentation_ids(self):
         trunk = self._create_trunk_with_network_and_parent([])
@@ -131,7 +130,7 @@
                             'segmentation_id': 2,
                             'segmentation_type': 'vlan'}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('6f132ccc-1380-42d8-9c44-50411612bd01')
     def test_add_subport_port_id_uses_trunk_port_id(self):
         trunk = self._create_trunk_with_network_and_parent(None)
@@ -141,7 +140,7 @@
                             'segmentation_type': 'vlan',
                             'segmentation_id': 2}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('7f132ccc-1380-42d8-9c44-50411612bd01')
     def test_add_subport_port_id_disabled_trunk(self):
         trunk = self._create_trunk_with_network_and_parent(
@@ -155,7 +154,7 @@
         self.client.update_trunk(
             trunk['trunk']['id'], admin_state_up=True)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('8f132ccc-1380-42d8-9c44-50411612bd01')
     def test_remove_subport_port_id_disabled_trunk(self):
         trunk = self._create_trunk_with_network_and_parent(
@@ -169,7 +168,7 @@
         self.client.update_trunk(
             trunk['trunk']['id'], admin_state_up=True)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('9f132ccc-1380-42d8-9c44-50411612bd01')
     def test_delete_trunk_disabled_trunk(self):
         trunk = self._create_trunk_with_network_and_parent(
@@ -180,7 +179,7 @@
         self.client.update_trunk(
             trunk['trunk']['id'], admin_state_up=True)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('00cb40bb-1593-44c8-808c-72b47e64252f')
     def test_add_subport_duplicate_segmentation_details(self):
         trunk = self._create_trunk_with_network_and_parent(None)
@@ -197,7 +196,7 @@
                             'segmentation_type': 'vlan',
                             'segmentation_id': 2}])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('4eac8c25-83ee-4051-9620-34774f565730')
     def test_add_subport_passing_dict(self):
         trunk = self._create_trunk_with_network_and_parent(None)
@@ -207,7 +206,7 @@
                            'segmentation_type': 'vlan',
                            'segmentation_id': 2})
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('17ca7dd7-96a8-445a-941e-53c0c86c2fe2')
     def test_remove_subport_passing_dict(self):
         network = self.create_network()
@@ -219,7 +218,7 @@
         self.assertRaises(lib_exc.BadRequest, self.client.remove_subports,
                           trunk['trunk']['id'], subport_data)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('aaca7dd7-96b8-445a-931e-63f0d86d2fe2')
     def test_remove_subport_not_found(self):
         network = self.create_network()
@@ -231,14 +230,14 @@
         self.assertRaises(lib_exc.NotFound, self.client.remove_subports,
                           trunk['trunk']['id'], [subport_data])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('6c9c5126-4f61-11e6-8248-40a8f063c891')
     def test_delete_port_in_use_by_trunk(self):
         trunk = self._create_trunk_with_network_and_parent(None)
         self.assertRaises(lib_exc.Conflict, self.client.delete_port,
                           trunk['trunk']['port_id'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('343a03d0-4f7c-11e6-97fa-40a8f063c891')
     def test_delete_port_in_use_by_subport(self):
         network = self.create_network()
@@ -256,7 +255,7 @@
     required_extensions = (
         ['net-mtu'] + test_trunk.TrunkTestMtusJSONBase.required_extensions)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('228380ef-1b7a-495e-b759-5b1f08e3e858')
     def test_create_trunk_with_mtu_smaller_than_subport(self):
         subports = [{'port_id': self.larger_mtu_port['id'],
@@ -268,7 +267,7 @@
                                              subports)
             self.trunks.append(trunk['trunk'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('3b32bf77-8002-403e-ad01-6f4cf018daa5')
     def test_add_subport_with_mtu_greater_than_trunk(self):
         subports = [{'port_id': self.larger_mtu_port['id'],
diff --git a/neutron/tests/tempest/config.py b/neutron/tests/tempest/config.py
index 5a9ac77..6830b18 100644
--- a/neutron/tests/tempest/config.py
+++ b/neutron/tests/tempest/config.py
@@ -30,6 +30,10 @@
                 default=[],
                 help='List of network types available to neutron, '
                      'e.g. vxlan,vlan,gre.'),
+    cfg.BoolOpt('image_is_advanced',
+                default=False,
+                help='Image that supports features that cirros does not, like'
+                     ' Ubuntu or CentOS supporting advanced features'),
 ]
 
 # TODO(amuller): Redo configuration options registration as part of the planned
diff --git a/neutron/tests/tempest/scenario/test_trunk.py b/neutron/tests/tempest/scenario/test_trunk.py
index 5d775ef..1412b11 100644
--- a/neutron/tests/tempest/scenario/test_trunk.py
+++ b/neutron/tests/tempest/scenario/test_trunk.py
@@ -18,6 +18,7 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest import test
+import testtools
 
 from neutron.common import utils
 from neutron.tests.tempest.common import ssh
@@ -221,6 +222,9 @@
                                 CONF.validation.image_ssh_user,
                                 self.keypair['private_key'])
 
+    @testtools.skipUnless(
+          CONF.neutron_plugin_options.image_is_advanced,
+          "Advanced image is required to run this test.")
     @decorators.idempotent_id('a8a02c9b-b453-49b5-89a2-cce7da66aafb')
     def test_subport_connectivity(self):
         vlan_tag = 10
diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py
index 39f2c38..12cdf97 100644
--- a/neutron/tests/tempest/services/network/json/network_client.py
+++ b/neutron/tests/tempest/services/network/json/network_client.py
@@ -124,7 +124,13 @@
             # list of field's name. An example:
             # {'fields': ['id', 'name']}
             plural = self.pluralize(resource_name)
-            uri = '%s/%s' % (self.get_uri(plural), resource_id)
+            if 'details_quotas' in plural:
+                details, plural = plural.split('_')
+                uri = '%s/%s/%s' % (self.get_uri(plural),
+                                    resource_id, details)
+            else:
+                uri = '%s/%s' % (self.get_uri(plural), resource_id)
+
             if fields:
                 uri += '?' + urlparse.urlencode(fields, doseq=1)
             resp, body = self.get(uri)