Merge "Use more specific asserts in tests"
diff --git a/neutron/tests/tempest/api/admin/test_networks.py b/neutron/tests/tempest/api/admin/test_networks.py
new file mode 100644
index 0000000..1068c0b
--- /dev/null
+++ b/neutron/tests/tempest/api/admin/test_networks.py
@@ -0,0 +1,70 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_utils import uuidutils
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+
+from neutron.tests.tempest.api import base
+
+
+class NetworksTestAdmin(base.BaseAdminNetworkTest):
+
+    @test.idempotent_id('d3c76044-d067-4cb0-ae47-8cdd875c7f67')
+    @test.requires_ext(extension="project-id", service="network")
+    def test_admin_create_network_keystone_v3(self):
+        project_id = self.client.tenant_id  # non-admin
+
+        name = 'admin-created-with-project_id'
+        new_net = self.create_network_keystone_v3(name, project_id,
+            client=self.admin_client)
+        self.assertEqual(name, new_net['name'])
+        self.assertEqual(project_id, new_net['project_id'])
+        self.assertEqual(project_id, new_net['tenant_id'])
+
+        body = self.client.list_networks(id=new_net['id'])
+        lookup_net = body['networks'][0]
+        self.assertEqual(name, lookup_net['name'])
+        self.assertEqual(project_id, lookup_net['project_id'])
+        self.assertEqual(project_id, lookup_net['tenant_id'])
+
+    @test.idempotent_id('8d21aaca-4364-4eb9-8b79-44b4fff6373b')
+    @test.requires_ext(extension="project-id", service="network")
+    def test_admin_create_network_keystone_v3_and_tenant(self):
+        project_id = self.client.tenant_id  # non-admin
+
+        name = 'created-with-project-and-tenant'
+        new_net = self.create_network_keystone_v3(
+            name, project_id, tenant_id=project_id, client=self.admin_client)
+        self.assertEqual(name, new_net['name'])
+        self.assertEqual(project_id, new_net['project_id'])
+        self.assertEqual(project_id, new_net['tenant_id'])
+
+        body = self.client.list_networks(id=new_net['id'])
+        lookup_net = body['networks'][0]
+        self.assertEqual(name, lookup_net['name'])
+        self.assertEqual(project_id, lookup_net['project_id'])
+        self.assertEqual(project_id, lookup_net['tenant_id'])
+
+    @test.idempotent_id('08b92179-669d-45ee-8233-ef6611190809')
+    @test.requires_ext(extension="project-id", service="network")
+    def test_admin_create_network_keystone_v3_and_other_tenant(self):
+        project_id = self.client.tenant_id  # non-admin
+        other_tenant = uuidutils.generate_uuid()
+
+        name = 'created-with-project-and-other-tenant'
+        e = self.assertRaises(lib_exc.BadRequest,
+                              self.create_network_keystone_v3, name,
+                              project_id, tenant_id=other_tenant,
+                              client=self.admin_client)
+        expected_message = "'project_id' and 'tenant_id' do not match"
+        self.assertEqual(expected_message, e.resp_body['message'])
diff --git a/neutron/tests/tempest/api/admin/test_quotas_negative.py b/neutron/tests/tempest/api/admin/test_quotas_negative.py
index 12ae0be..3ff49c6 100644
--- a/neutron/tests/tempest/api/admin/test_quotas_negative.py
+++ b/neutron/tests/tempest/api/admin/test_quotas_negative.py
@@ -71,6 +71,7 @@
 
         subnet_args = {'tenant_id': tenant_id,
                        'network_id': net['id'],
+                       'enable_dhcp': False,
                        'cidr': '10.0.0.0/24',
                        'ip_version': '4'}
         subnet = self.admin_client.create_subnet(**subnet_args)['subnet']
diff --git a/neutron/tests/tempest/api/admin/test_routers_flavors.py b/neutron/tests/tempest/api/admin/test_routers_flavors.py
new file mode 100644
index 0000000..300c956
--- /dev/null
+++ b/neutron/tests/tempest/api/admin/test_routers_flavors.py
@@ -0,0 +1,97 @@
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+import testtools
+
+from neutron.tests.tempest.api import base_routers as base
+
+
+class RoutersFlavorTestCase(base.BaseRouterTest):
+
+    @classmethod
+    @test.requires_ext(extension="router", service="network")
+    @test.requires_ext(extension="flavors", service="network")
+    def skip_checks(cls):
+        super(RoutersFlavorTestCase, cls).skip_checks()
+
+    @classmethod
+    def resource_setup(cls):
+        super(RoutersFlavorTestCase, cls).resource_setup()
+        cls.service_profiles = []
+        cls.flavor_service_profiles = []
+        # make a flavor based on legacy router for regular tenant to use
+        driver = ('neutron.services.l3_router.service_providers.'
+                  'single_node.SingleNodeDriver')
+        sp = cls.admin_client.create_service_profile(driver=driver)
+        cls.service_profiles.append(sp['service_profile'])
+        cls.flavor = cls.create_flavor(
+                name='special_flavor',
+                description='econonomy class',
+                service_type='L3_ROUTER_NAT')
+        cls.admin_client.create_flavor_service_profile(
+            cls.flavor['id'], sp['service_profile']['id'])
+        cls.flavor_service_profiles.append((cls.flavor['id'],
+                                            sp['service_profile']['id']))
+        # make another with a different driver
+        driver = ('neutron.services.l3_router.service_providers.'
+                  'dvr.DvrDriver')
+        sp = cls.admin_client.create_service_profile(driver=driver)
+        cls.service_profiles.append(sp['service_profile'])
+        cls.prem_flavor = cls.create_flavor(
+                name='better_special_flavor',
+                description='econonomy comfort',
+                service_type='L3_ROUTER_NAT')
+        cls.admin_client.create_flavor_service_profile(
+            cls.prem_flavor['id'], sp['service_profile']['id'])
+        cls.flavor_service_profiles.append((cls.prem_flavor['id'],
+                                            sp['service_profile']['id']))
+
+    @classmethod
+    def resource_cleanup(cls):
+        for flavor_id, service_profile_id in cls.flavor_service_profiles:
+            cls.admin_client.delete_flavor_service_profile(flavor_id,
+                                                           service_profile_id)
+        for service_profile in cls.service_profiles:
+            cls.admin_client.delete_service_profile(
+                service_profile['id'])
+        super(RoutersFlavorTestCase, cls).resource_cleanup()
+
+    @test.idempotent_id('a4d01977-e968-4983-b4d9-824ea6c33f4b')
+    def test_create_router_with_flavor(self):
+        # ensure regular client can see flavor
+        flavors = self.client.list_flavors(id=self.flavor['id'])
+        flavor = flavors['flavors'][0]
+        self.assertEqual('special_flavor', flavor['name'])
+        flavors = self.client.list_flavors(id=self.prem_flavor['id'])
+        prem_flavor = flavors['flavors'][0]
+        self.assertEqual('better_special_flavor', prem_flavor['name'])
+
+        # ensure client can create router with both flavors
+        router = self.create_router('name', flavor_id=flavor['id'])
+        self.assertEqual(flavor['id'], router['flavor_id'])
+        router = self.create_router('name', flavor_id=prem_flavor['id'])
+        self.assertEqual(prem_flavor['id'], router['flavor_id'])
+
+    @test.idempotent_id('30e73858-a0fc-409c-a2e0-e9cd2826f6a2')
+    def test_delete_router_flavor_in_use(self):
+        self.create_router('name', flavor_id=self.flavor['id'])
+        with testtools.ExpectedException(lib_exc.Conflict):
+            self.admin_client.delete_flavor(self.flavor['id'])
+
+    @test.idempotent_id('83939cf7-5070-41bc-9a3e-cd9f22df2186')
+    def test_badrequest_on_requesting_flags_and_flavor(self):
+        with testtools.ExpectedException(lib_exc.BadRequest):
+            self.admin_client.create_router(
+                'name', flavor_id=self.flavor['id'], distributed=True)
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 a637c3e..bc10745 100644
--- a/neutron/tests/tempest/api/admin/test_shared_network_extension.py
+++ b/neutron/tests/tempest/api/admin/test_shared_network_extension.py
@@ -192,6 +192,22 @@
         )['rbac_policy']
         return {'network': net, 'subnet': subnet, 'policy': pol}
 
+    @test.attr(type='smoke')
+    @test.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')
+    @test.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):
+            target_tenant = '1234' * 100
+            self._make_admin_net_and_subnet_shared_to_tenant_id(
+                tenant_id=target_tenant)
+
+    @test.attr(type='smoke')
     @test.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(
diff --git a/neutron/tests/tempest/api/base.py b/neutron/tests/tempest/api/base.py
index c4d3b48..b308a31 100644
--- a/neutron/tests/tempest/api/base.py
+++ b/neutron/tests/tempest/api/base.py
@@ -93,7 +93,7 @@
         super(BaseNetworkTest, cls).resource_setup()
 
         cls.networks = []
-        cls.shared_networks = []
+        cls.admin_networks = []
         cls.subnets = []
         cls.ports = []
         cls.routers = []
@@ -162,8 +162,8 @@
                 cls._try_delete_resource(cls.client.delete_network,
                                          network['id'])
 
-            # Clean up shared networks
-            for network in cls.shared_networks:
+            # Clean up admin networks
+            for network in cls.admin_networks:
                 cls._try_delete_resource(cls.admin_client.delete_network,
                                          network['id'])
 
@@ -222,7 +222,24 @@
         post_body.update({'name': network_name, 'shared': True})
         body = cls.admin_client.create_network(**post_body)
         network = body['network']
-        cls.shared_networks.append(network)
+        cls.admin_networks.append(network)
+        return network
+
+    @classmethod
+    def create_network_keystone_v3(cls, network_name=None, project_id=None,
+                                   tenant_id=None, client=None):
+        """Wrapper utility that creates a test network with project_id."""
+        client = client or cls.client
+        network_name = network_name or data_utils.rand_name(
+            'test-network-with-project_id')
+        project_id = cls.client.tenant_id
+        body = client.create_network_keystone_v3(network_name, project_id,
+            tenant_id)
+        network = body['network']
+        if client is cls.client:
+            cls.networks.append(network)
+        else:
+            cls.admin_networks.append(network)
         return network
 
     @classmethod
@@ -728,3 +745,12 @@
             # marker
             expected_resources[:-1],
             self._extract_resources(body))
+
+    def _test_list_validation_filters(self):
+        validation_args = {
+            'unknown_filter': 'value',
+        }
+        body = self.list_method(**validation_args)
+        resources = self._extract_resources(body)
+        for resource in resources:
+            self.assertIn(resource['name'], self.resource_names)
diff --git a/neutron/tests/tempest/api/base_security_groups.py b/neutron/tests/tempest/api/base_security_groups.py
index 8575ba9..5028ec1 100644
--- a/neutron/tests/tempest/api/base_security_groups.py
+++ b/neutron/tests/tempest/api/base_security_groups.py
@@ -20,10 +20,6 @@
 
 class BaseSecGroupTest(base.BaseNetworkTest):
 
-    @classmethod
-    def resource_setup(cls):
-        super(BaseSecGroupTest, cls).resource_setup()
-
     def _create_security_group(self, **kwargs):
         # Create a security group
         name = data_utils.rand_name('secgroup-')
diff --git a/neutron/tests/tempest/api/clients.py b/neutron/tests/tempest/api/clients.py
index eb39882..c6f41d0 100644
--- a/neutron/tests/tempest/api/clients.py
+++ b/neutron/tests/tempest/api/clients.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_config import cfg
-
 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
@@ -30,22 +28,12 @@
     """
     Top level manager for OpenStack tempest clients
     """
-    try:
-        default_params = {
-            'disable_ssl_certificate_validation':
-                CONF.service_clients.disable_ssl_certificate_validation,
-            'ca_certs': CONF.service_clients.ca_certificates_file,
-            'trace_requests': CONF.debug.trace_requests
-        }
-    except cfg.NoSuchOptError:
-        # TODO(armax): remove this except when a new tempest release
-        # > 12.1.0 includes change 1afca56b05
-        default_params = {
-            'disable_ssl_certificate_validation':
-                CONF.identity.disable_ssl_certificate_validation,
-            'ca_certs': CONF.identity.ca_certificates_file,
-            'trace_requests': CONF.debug.trace_requests
-        }
+    default_params = {
+        'disable_ssl_certificate_validation':
+            CONF.identity.disable_ssl_certificate_validation,
+        'ca_certs': CONF.identity.ca_certificates_file,
+        'trace_requests': CONF.debug.trace_requests
+    }
 
     # NOTE: Tempest uses timeout values of compute API if project specific
     # timeout values don't exist.
diff --git a/neutron/tests/tempest/api/test_allowed_address_pair.py b/neutron/tests/tempest/api/test_allowed_address_pair.py
index 88818b2..5313785 100644
--- a/neutron/tests/tempest/api/test_allowed_address_pair.py
+++ b/neutron/tests/tempest/api/test_allowed_address_pair.py
@@ -19,8 +19,6 @@
 from neutron.tests.tempest.api import base
 from neutron.tests.tempest import config
 
-CONF = config.CONF
-
 
 class AllowedAddressPairTestJSON(base.BaseNetworkTest):
 
@@ -85,7 +83,7 @@
         body = self.client.update_port(
             port_id, allowed_address_pairs=allowed_address_pairs)
         allowed_address_pair = body['port']['allowed_address_pairs']
-        self.assertEqual(allowed_address_pair, allowed_address_pairs)
+        self.assertItemsEqual(allowed_address_pair, allowed_address_pairs)
 
     @test.idempotent_id('9599b337-272c-47fd-b3cf-509414414ac4')
     def test_update_port_with_address_pair(self):
diff --git a/neutron/tests/tempest/api/test_auto_allocated_topology.py b/neutron/tests/tempest/api/test_auto_allocated_topology.py
index afdfe8c..65c3057 100644
--- a/neutron/tests/tempest/api/test_auto_allocated_topology.py
+++ b/neutron/tests/tempest/api/test_auto_allocated_topology.py
@@ -22,11 +22,14 @@
 class TestAutoAllocatedTopology(base.BaseAdminNetworkTest):
 
     """
-    NOTE: This test may eventually migrate to Tempest.
-
-    Tests the Get-Me-A-Network operation in the Neutron API
+    Tests the Get-Me-A-Network operations in the Neutron API
     using the REST client for Neutron.
     """
+    # NOTE(armax): this is a precaution to avoid interference
+    # from other tests exercising this extension. So long as
+    # all tests are added under TestAutoAllocatedTopology,
+    # nothing bad should happen.
+    force_tenant_isolation = True
 
     @classmethod
     @test.requires_ext(extension="auto-allocated-topology", service="network")
@@ -101,3 +104,14 @@
         # After the initial GET, the API should be idempotent
         self.assertEqual(network_id1, network_id2)
         self.assertEqual(resources_after1, resources_after2)
+
+    @test.idempotent_id('aabc0b02-cee4-11e5-9f3c-091127605a2b')
+    def test_delete_allocated_net_topology_as_tenant(self):
+        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']
+        self.assertIsNotNone(topology)
+        self.client.delete_auto_allocated_topology()
+        resources_after = self._count_topology_resources()
+        self.assertEqual((0, 0, 0), resources_after)
diff --git a/neutron/tests/tempest/api/test_extensions.py b/neutron/tests/tempest/api/test_extensions.py
index 1defb33..b1b09e8 100644
--- a/neutron/tests/tempest/api/test_extensions.py
+++ b/neutron/tests/tempest/api/test_extensions.py
@@ -13,9 +13,6 @@
 from tempest import test
 
 from neutron.tests.tempest.api import base
-from neutron.tests.tempest import config
-
-CONF = config.CONF
 
 
 class ExtensionsTest(base.BaseNetworkTest):
@@ -37,3 +34,7 @@
     @test.idempotent_id('19db409e-a23f-445d-8bc8-ca3d64c84706')
     def test_list_extensions_pagination(self):
         self._test_list_extensions_includes('pagination')
+
+    @test.idempotent_id('155b7bc2-e358-4dd8-bf3e-1774c084567f')
+    def test_list_extensions_project_id(self):
+        self._test_list_extensions_includes('project-id')
diff --git a/neutron/tests/tempest/api/test_floating_ips.py b/neutron/tests/tempest/api/test_floating_ips.py
index 8ccdd44..bafa54c 100644
--- a/neutron/tests/tempest/api/test_floating_ips.py
+++ b/neutron/tests/tempest/api/test_floating_ips.py
@@ -71,3 +71,33 @@
         self.assertEqual('d2', body['floatingip']['description'])
         body = self.client.show_floatingip(body['floatingip']['id'])
         self.assertEqual('d2', body['floatingip']['description'])
+        # disassociate
+        body = self.client.update_floatingip(body['floatingip']['id'],
+                                             port_id=None)
+        self.assertEqual('d2', body['floatingip']['description'])
+
+    @test.idempotent_id('fd7161e1-2167-4686-a6ff-0f3df08001bb')
+    @test.requires_ext(extension="standard-attr-description",
+                       service="network")
+    def test_floatingip_update_extra_attributes_port_id_not_changed(self):
+        port_id = self.ports[1]['id']
+        body = self.client.create_floatingip(
+            floating_network_id=self.ext_net_id,
+            port_id=port_id,
+            description='d1'
+        )['floatingip']
+        self.assertEqual('d1', body['description'])
+        body = self.client.show_floatingip(body['id'])['floatingip']
+        self.assertEqual(port_id, body['port_id'])
+        # Update description
+        body = self.client.update_floatingip(body['id'], description='d2')
+        self.assertEqual('d2', body['floatingip']['description'])
+        # Floating IP association is not changed.
+        self.assertEqual(port_id, body['floatingip']['port_id'])
+        body = self.client.show_floatingip(body['floatingip']['id'])
+        self.assertEqual('d2', body['floatingip']['description'])
+        self.assertEqual(port_id, body['floatingip']['port_id'])
+        # disassociate
+        body = self.client.update_floatingip(body['floatingip']['id'],
+                                             port_id=None)
+        self.assertEqual(None, body['floatingip']['port_id'])
diff --git a/neutron/tests/tempest/api/test_metering_extensions.py b/neutron/tests/tempest/api/test_metering_extensions.py
index 756cd5a..7b03386 100644
--- a/neutron/tests/tempest/api/test_metering_extensions.py
+++ b/neutron/tests/tempest/api/test_metering_extensions.py
@@ -15,8 +15,11 @@
 from tempest.lib.common.utils import data_utils
 from tempest import test
 
+from neutron.api.v2 import attributes as attr
 from neutron.tests.tempest.api import base
 
+LONG_NAME_OK = 'x' * (attr.NAME_MAX_LEN)
+
 
 class MeteringTestJSON(base.BaseAdminNetworkTest):
 
@@ -81,6 +84,17 @@
                   id=metering_label['id']))
         self.assertEqual(len(labels['metering_labels']), 1)
 
+    @test.idempotent_id('46608f8d-2e27-4eb6-a0b4-dbe405144c4d')
+    def test_create_delete_metering_label_with_name_max_length(self):
+        name = LONG_NAME_OK
+        body = self.admin_client.create_metering_label(name=name)
+        metering_label = body['metering_label']
+        self.addCleanup(self._delete_metering_label,
+                        metering_label['id'])
+        labels = (self.admin_client.list_metering_labels(
+                  id=metering_label['id']))
+        self.assertEqual(len(labels['metering_labels']), 1)
+
     @test.idempotent_id('cfc500d9-9de6-4847-8803-62889c097d45')
     def test_show_metering_label(self):
         # Verifies the details of a label
diff --git a/neutron/tests/tempest/api/test_metering_negative.py b/neutron/tests/tempest/api/test_metering_negative.py
new file mode 100644
index 0000000..39fdae8
--- /dev/null
+++ b/neutron/tests/tempest/api/test_metering_negative.py
@@ -0,0 +1,36 @@
+# Copyright 2016 FUJITSU LIMITED
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+
+from neutron.api.v2 import attributes as attr
+from neutron.tests.tempest.api import base
+
+LONG_NAME_NG = 'x' * (attr.NAME_MAX_LEN + 1)
+
+
+class MeteringNegativeTestJSON(base.BaseAdminNetworkTest):
+
+    @classmethod
+    @test.requires_ext(extension="metering", service="network")
+    def resource_setup(cls):
+        super(MeteringNegativeTestJSON, cls).resource_setup()
+
+    @test.attr(type='negative')
+    @test.idempotent_id('8b3f7c84-9d37-4771-8681-bfd2c07f3c2d')
+    def test_create_metering_label_with_too_long_name(self):
+        self.assertRaises(lib_exc.BadRequest,
+                          self.admin_client.create_metering_label,
+                          name=LONG_NAME_NG)
diff --git a/neutron/tests/tempest/api/test_network_ip_availability.py b/neutron/tests/tempest/api/test_network_ip_availability.py
index 6a81128..324c3fd 100644
--- a/neutron/tests/tempest/api/test_network_ip_availability.py
+++ b/neutron/tests/tempest/api/test_network_ip_availability.py
@@ -24,8 +24,6 @@
 
 from neutron_lib import constants as lib_constants
 
-CONF = config.CONF
-
 # 3 IP addresses are taken from every total for IPv4 these are reserved
 DEFAULT_IP4_RESERVED = 3
 # 2 IP addresses are taken from every total for IPv6 these are reserved
diff --git a/neutron/tests/tempest/api/test_networks.py b/neutron/tests/tempest/api/test_networks.py
index 16fe81b..279964a 100644
--- a/neutron/tests/tempest/api/test_networks.py
+++ b/neutron/tests/tempest/api/test_networks.py
@@ -16,9 +16,6 @@
 from tempest import test
 
 from neutron.tests.tempest.api import base
-from neutron.tests.tempest import config
-
-CONF = config.CONF
 
 
 class NetworksTestJSON(base.BaseNetworkTest):
@@ -49,6 +46,10 @@
             fields.append('mtu')
         for key in fields:
             self.assertEqual(network[key], self.network[key])
+        project_id = self.client.tenant_id
+        self.assertEqual(project_id, network['tenant_id'])
+        if test.is_extension_enabled('project-id', 'network'):
+            self.assertEqual(project_id, network['project_id'])
 
     @test.idempotent_id('867819bb-c4b6-45f7-acf9-90edcf70aa5e')
     def test_show_network_fields(self):
@@ -62,6 +63,27 @@
         self.assertEqual(sorted(network.keys()), sorted(fields))
         for field_name in fields:
             self.assertEqual(network[field_name], self.network[field_name])
+        self.assertNotIn('tenant_id', network)
+        self.assertNotIn('project_id', network)
+
+    @test.idempotent_id('26f2b7a5-2cd1-4f3a-b11f-ad259b099b11')
+    @test.requires_ext(extension="project-id", service="network")
+    def test_show_network_fields_keystone_v3(self):
+
+        def _check_show_network_fields(fields, expect_project_id,
+                                       expect_tenant_id):
+            params = {}
+            if fields:
+                params['fields'] = fields
+            body = self.client.show_network(self.network['id'], **params)
+            network = body['network']
+            self.assertEqual(expect_project_id, 'project_id' in network)
+            self.assertEqual(expect_tenant_id, 'tenant_id' in network)
+
+        _check_show_network_fields(None, True, True)
+        _check_show_network_fields(['tenant_id'], False, True)
+        _check_show_network_fields(['project_id'], True, False)
+        _check_show_network_fields(['project_id', 'tenant_id'], True, True)
 
     @test.idempotent_id('c72c1c0c-2193-4aca-ccc4-b1442640bbbb')
     @test.requires_ext(extension="standard-attr-description",
@@ -78,6 +100,27 @@
         body = self.client.list_networks(id=net_id)['networks'][0]
         self.assertEqual('d2', body['description'])
 
+    @test.idempotent_id('0cc0552f-afaf-4231-b7a7-c2a1774616da')
+    @test.requires_ext(extension="project-id", service="network")
+    def test_create_network_keystone_v3(self):
+        project_id = self.client.tenant_id
+
+        name = 'created-with-project_id'
+        new_net = self.create_network_keystone_v3(name, project_id)
+        self.assertEqual(name, new_net['name'])
+        self.assertEqual(project_id, new_net['project_id'])
+        self.assertEqual(project_id, new_net['tenant_id'])
+
+        body = self.client.list_networks(id=new_net['id'])['networks'][0]
+        self.assertEqual(name, body['name'])
+
+        new_name = 'create-with-project_id-2'
+        body = self.client.update_network(new_net['id'], name=new_name)
+        new_net = body['network']
+        self.assertEqual(new_name, new_net['name'])
+        self.assertEqual(project_id, new_net['project_id'])
+        self.assertEqual(project_id, new_net['tenant_id'])
+
     @test.idempotent_id('6ae6d24f-9194-4869-9c85-c313cb20e080')
     def test_list_networks_fields(self):
         # Verify specific fields of the networks
@@ -90,12 +133,32 @@
         for network in networks:
             self.assertEqual(sorted(network.keys()), sorted(fields))
 
+    @test.idempotent_id('a23186b9-aa6f-4b08-b877-35ca3b9cd54c')
+    @test.requires_ext(extension="project-id", service="network")
+    def test_list_networks_fields_keystone_v3(self):
+        def _check_list_networks_fields(fields, expect_project_id,
+                                        expect_tenant_id):
+            params = {}
+            if fields:
+                params['fields'] = fields
+            body = self.client.list_networks(**params)
+            networks = body['networks']
+            self.assertNotEmpty(networks, "Network list returned is empty")
+            for network in networks:
+                self.assertEqual(expect_project_id, 'project_id' in network)
+                self.assertEqual(expect_tenant_id, 'tenant_id' in network)
+
+        _check_list_networks_fields(None, True, True)
+        _check_list_networks_fields(['tenant_id'], False, True)
+        _check_list_networks_fields(['project_id'], True, False)
+        _check_list_networks_fields(['project_id', 'tenant_id'], True, True)
+
 
 class NetworksSearchCriteriaTest(base.BaseSearchCriteriaTest):
 
     resource = 'network'
 
-    list_kwargs = {'shared': False}
+    list_kwargs = {'shared': False, 'router:external': False}
 
     @classmethod
     def resource_setup(cls):
@@ -138,3 +201,7 @@
     @test.idempotent_id('f1867fc5-e1d6-431f-bc9f-8b882e43a7f9')
     def test_list_no_pagination_limit_0(self):
         self._test_list_no_pagination_limit_0()
+
+    @test.idempotent_id('3574ec9b-a8b8-43e3-9c11-98f5875df6a9')
+    def test_list_validation_filters(self):
+        self._test_list_validation_filters()
diff --git a/neutron/tests/tempest/api/test_ports.py b/neutron/tests/tempest/api/test_ports.py
index 916f549..093de07 100644
--- a/neutron/tests/tempest/api/test_ports.py
+++ b/neutron/tests/tempest/api/test_ports.py
@@ -47,6 +47,18 @@
         self.client.update_subnet(s['id'], enable_dhcp=True)
         self.create_port(self.network)
 
+    @test.idempotent_id('1d6d8683-8691-43c6-a7ba-c69723258726')
+    def test_add_ips_to_port(self):
+        s = self.create_subnet(self.network)
+        port = self.create_port(self.network)
+        # request another IP on the same subnet
+        port['fixed_ips'].append({'subnet_id': s['id']})
+        updated = self.client.update_port(port['id'],
+                                          fixed_ips=port['fixed_ips'])
+        subnets = [ip['subnet_id'] for ip in updated['port']['fixed_ips']]
+        expected = [s['id'], s['id']]
+        self.assertEqual(expected, subnets)
+
 
 class PortsSearchCriteriaTest(base.BaseSearchCriteriaTest):
 
diff --git a/neutron/tests/tempest/api/test_qos.py b/neutron/tests/tempest/api/test_qos.py
index 313715d..b9572ca 100644
--- a/neutron/tests/tempest/api/test_qos.py
+++ b/neutron/tests/tempest/api/test_qos.py
@@ -75,6 +75,28 @@
         self.assertTrue(retrieved_policy['shared'])
         self.assertEqual([], retrieved_policy['rules'])
 
+    @test.idempotent_id('6e880e0f-bbfc-4e54-87c6-680f90e1b618')
+    def test_policy_update_forbidden_for_regular_tenants_own_policy(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='',
+                                        shared=False,
+                                        tenant_id=self.client.tenant_id)
+        self.assertRaises(
+            exceptions.Forbidden,
+            self.client.update_qos_policy,
+            policy['id'], description='test policy')
+
+    @test.idempotent_id('4ecfd7e7-47b6-4702-be38-be9235901a87')
+    def test_policy_update_forbidden_for_regular_tenants_foreign_policy(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='',
+                                        shared=False,
+                                        tenant_id=self.admin_client.tenant_id)
+        self.assertRaises(
+            exceptions.NotFound,
+            self.client.update_qos_policy,
+            policy['id'], description='test policy')
+
     @test.idempotent_id('ee263db4-009a-4641-83e5-d0e83506ba4c')
     def test_shared_policy_update(self):
         policy = self.create_qos_policy(name='test-policy',
@@ -315,6 +337,13 @@
         obtained_policy = self.client.show_qos_policy(policy['id'])['policy']
         self.assertEqual(obtained_policy, policy)
 
+    @test.idempotent_id('aed8e2a6-22da-421b-89b9-935a2c1a1b50')
+    def test_policy_create_forbidden_for_regular_tenants(self):
+        self.assertRaises(
+            exceptions.Forbidden,
+            self.client.create_qos_policy,
+            'test-policy', 'test policy', False)
+
 
 class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
     @classmethod
@@ -412,13 +441,6 @@
             self.create_qos_bandwidth_limit_rule,
             'policy', 200, 1337)
 
-    @test.idempotent_id('eed8e2a6-22da-421b-89b9-935a2c1a1b50')
-    def test_policy_create_forbidden_for_regular_tenants(self):
-        self.assertRaises(
-            exceptions.Forbidden,
-            self.client.create_qos_policy,
-            'test-policy', 'test policy', False)
-
     @test.idempotent_id('a4a2e7ad-786f-4927-a85a-e545a93bd274')
     def test_rule_create_forbidden_for_regular_tenants(self):
         self.assertRaises(
@@ -426,6 +448,34 @@
             self.client.create_bandwidth_limit_rule,
             'policy', 1, 2)
 
+    @test.idempotent_id('1bfc55d9-6fd8-4293-ab3a-b1d69bf7cd2e')
+    def test_rule_update_forbidden_for_regular_tenants_own_policy(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False,
+                                        tenant_id=self.client.tenant_id)
+        rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
+                                                    max_kbps=1,
+                                                    max_burst_kbps=1)
+        self.assertRaises(
+            exceptions.NotFound,
+            self.client.update_bandwidth_limit_rule,
+            policy['id'], rule['id'], max_kbps=2, max_burst_kbps=4)
+
+    @test.idempotent_id('9a607936-4b6f-4c2f-ad21-bd5b3d4fc91f')
+    def test_rule_update_forbidden_for_regular_tenants_foreign_policy(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False,
+                                        tenant_id=self.admin_client.tenant_id)
+        rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
+                                                    max_kbps=1,
+                                                    max_burst_kbps=1)
+        self.assertRaises(
+            exceptions.NotFound,
+            self.client.update_bandwidth_limit_rule,
+            policy['id'], rule['id'], max_kbps=2, max_burst_kbps=4)
+
     @test.idempotent_id('ce0bd0c2-54d9-4e29-85f1-cfb36ac3ebe2')
     def test_get_rules_by_policy(self):
         policy1 = self.create_qos_policy(name='test-policy1',
@@ -504,6 +554,7 @@
         self.client2.show_qos_policy(qos_pol['id'])
         rbac_pol = {'target_tenant': '*',
                     'tenant_id': self.admin_client.tenant_id,
+                    'project_id': self.admin_client.tenant_id,
                     'object_type': 'qos_policy',
                     'object_id': qos_pol['id'],
                     'action': 'access_as_shared'}
@@ -833,6 +884,162 @@
         self.assertNotIn(rule2['id'], rules_ids)
 
 
+class QosMinimumBandwidthRuleTestJSON(base.BaseAdminNetworkTest):
+    DIRECTION_EGRESS = "egress"
+    DIRECTION_INGRESS = "ingress"
+    RULE_NAME = qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH + "_rule"
+    RULES_NAME = RULE_NAME + "s"
+
+    @classmethod
+    @test.requires_ext(extension="qos", service="network")
+    def resource_setup(cls):
+        super(QosMinimumBandwidthRuleTestJSON, cls).resource_setup()
+
+    @test.idempotent_id('aa59b00b-3e9c-4787-92f8-93a5cdf5e378')
+    def test_rule_create(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        rule = self.admin_client.create_minimum_bandwidth_rule(
+            policy_id=policy['id'],
+            direction=self.DIRECTION_EGRESS,
+            min_kbps=1138)[self.RULE_NAME]
+
+        # Test 'show rule'
+        retrieved_rule = self.admin_client.show_minimum_bandwidth_rule(
+            policy['id'], rule['id'])
+        retrieved_rule = retrieved_rule[self.RULE_NAME]
+        self.assertEqual(rule['id'], retrieved_rule['id'])
+        self.assertEqual(1138, retrieved_rule['min_kbps'])
+        self.assertEqual(self.DIRECTION_EGRESS, retrieved_rule['direction'])
+
+        # Test 'list rules'
+        rules = self.admin_client.list_minimum_bandwidth_rules(policy['id'])
+        rules = rules[self.RULES_NAME]
+        rules_ids = [r['id'] for r in rules]
+        self.assertIn(rule['id'], rules_ids)
+
+        # Test 'show policy'
+        retrieved_policy = self.admin_client.show_qos_policy(policy['id'])
+        policy_rules = retrieved_policy['policy']['rules']
+        self.assertEqual(1, len(policy_rules))
+        self.assertEqual(rule['id'], policy_rules[0]['id'])
+        self.assertEqual(qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH,
+                         policy_rules[0]['type'])
+
+    @test.idempotent_id('266d9b87-e51c-48bd-9aa7-8269573621be')
+    def test_rule_create_fail_for_missing_min_kbps(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        self.assertRaises(exceptions.BadRequest,
+                          self.admin_client.create_minimum_bandwidth_rule,
+                          policy_id=policy['id'],
+                          direction=self.DIRECTION_EGRESS)
+
+    @test.idempotent_id('aa59b00b-ab01-4787-92f8-93a5cdf5e378')
+    def test_rule_create_fail_for_the_same_type(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        self.admin_client.create_minimum_bandwidth_rule(
+            policy_id=policy['id'],
+            direction=self.DIRECTION_EGRESS, min_kbps=200)
+
+        self.assertRaises(exceptions.Conflict,
+                          self.admin_client.create_minimum_bandwidth_rule,
+                          policy_id=policy['id'],
+                          direction=self.DIRECTION_EGRESS, min_kbps=201)
+
+    @test.idempotent_id('d6fce764-e511-4fa6-9f86-f4b41cf142cf')
+    def test_rule_create_fail_for_direction_ingress(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        self.assertRaises(exceptions.BadRequest,
+                          self.admin_client.create_minimum_bandwidth_rule,
+                          policy_id=policy['id'],
+                          direction=self.DIRECTION_INGRESS,
+                          min_kbps=201)
+
+    @test.idempotent_id('a49a6988-2568-47d2-931e-2dbc858943b3')
+    def test_rule_update(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        rule = self.admin_client.create_minimum_bandwidth_rule(
+            policy_id=policy['id'],
+            direction=self.DIRECTION_EGRESS,
+            min_kbps=300)[self.RULE_NAME]
+
+        self.admin_client.update_minimum_bandwidth_rule(policy['id'],
+            rule['id'], min_kbps=350, direction=self.DIRECTION_EGRESS)
+
+        retrieved_policy = self.admin_client.show_minimum_bandwidth_rule(
+            policy['id'], rule['id'])
+        retrieved_policy = retrieved_policy[self.RULE_NAME]
+        self.assertEqual(350, retrieved_policy['min_kbps'])
+        self.assertEqual(self.DIRECTION_EGRESS, retrieved_policy['direction'])
+
+    @test.idempotent_id('a7ee6efd-7b33-4a68-927d-275b4f8ba958')
+    def test_rule_delete(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        rule = self.admin_client.create_minimum_bandwidth_rule(
+            policy['id'], self.DIRECTION_EGRESS, min_kbps=200)[self.RULE_NAME]
+
+        retrieved_policy = self.admin_client.show_minimum_bandwidth_rule(
+            policy['id'], rule['id'])
+        retrieved_policy = retrieved_policy[self.RULE_NAME]
+        self.assertEqual(rule['id'], retrieved_policy['id'])
+
+        self.admin_client.delete_minimum_bandwidth_rule(policy['id'],
+                                                        rule['id'])
+        self.assertRaises(exceptions.NotFound,
+                          self.admin_client.show_minimum_bandwidth_rule,
+                          policy['id'], rule['id'])
+
+    @test.idempotent_id('a211222c-5808-46cb-a961-983bbab6b852')
+    def test_rule_create_rule_nonexistent_policy(self):
+        self.assertRaises(
+            exceptions.NotFound,
+            self.admin_client.create_minimum_bandwidth_rule,
+            'policy', self.DIRECTION_EGRESS, min_kbps=200)
+
+    @test.idempotent_id('b4a2e7ad-786f-4927-a85a-e545a93bd274')
+    def test_rule_create_forbidden_for_regular_tenants(self):
+        self.assertRaises(
+            exceptions.Forbidden,
+            self.client.create_minimum_bandwidth_rule,
+            'policy', self.DIRECTION_EGRESS, min_kbps=300)
+
+    @test.idempotent_id('de0bd0c2-54d9-4e29-85f1-cfb36ac3ebe2')
+    def test_get_rules_by_policy(self):
+        policy1 = self.create_qos_policy(name='test-policy1',
+                                         description='test policy1',
+                                         shared=False)
+        rule1 = self.admin_client.create_minimum_bandwidth_rule(
+            policy_id=policy1['id'],
+            direction=self.DIRECTION_EGRESS,
+            min_kbps=200)[self.RULE_NAME]
+
+        policy2 = self.create_qos_policy(name='test-policy2',
+                                         description='test policy2',
+                                         shared=False)
+        rule2 = self.admin_client.create_minimum_bandwidth_rule(
+            policy_id=policy2['id'],
+            direction=self.DIRECTION_EGRESS,
+            min_kbps=5000)[self.RULE_NAME]
+
+        # Test 'list rules'
+        rules = self.admin_client.list_minimum_bandwidth_rules(policy1['id'])
+        rules = rules[self.RULES_NAME]
+        rules_ids = [r['id'] for r in rules]
+        self.assertIn(rule1['id'], rules_ids)
+        self.assertNotIn(rule2['id'], rules_ids)
+
+
 class QosSearchCriteriaTest(base.BaseSearchCriteriaTest,
                             base.BaseAdminNetworkTest):
 
diff --git a/neutron/tests/tempest/api/test_revisions.py b/neutron/tests/tempest/api/test_revisions.py
index 92f7866..a70b995 100644
--- a/neutron/tests/tempest/api/test_revisions.py
+++ b/neutron/tests/tempest/api/test_revisions.py
@@ -10,6 +10,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import netaddr
+
 from tempest import test
 
 from neutron.tests.tempest.api import base
@@ -20,50 +22,66 @@
 class TestRevisions(base.BaseAdminNetworkTest, bsg.BaseSecGroupTest):
 
     @classmethod
-    @test.requires_ext(extension="revisions", service="network")
+    @test.requires_ext(extension="standard-attr-revisions", service="network")
     def skip_checks(cls):
         super(TestRevisions, cls).skip_checks()
 
     @test.idempotent_id('4a26a4be-9c53-483c-bc50-b53f1db10ac6')
     def test_update_network_bumps_revision(self):
         net = self.create_network()
-        self.assertIn('revision', net)
+        self.assertIn('revision_number', net)
         updated = self.client.update_network(net['id'], name='newnet')
-        self.assertGreater(updated['network']['revision'], net['revision'])
+        self.assertGreater(updated['network']['revision_number'],
+                           net['revision_number'])
 
     @test.idempotent_id('cac7ecde-12d5-4331-9a03-420899dea077')
     def test_update_port_bumps_revision(self):
         net = self.create_network()
         port = self.create_port(net)
-        self.assertIn('revision', port)
+        self.assertIn('revision_number', port)
         updated = self.client.update_port(port['id'], name='newport')
-        self.assertGreater(updated['port']['revision'], port['revision'])
+        self.assertGreater(updated['port']['revision_number'],
+                           port['revision_number'])
 
     @test.idempotent_id('c1c4fa41-8e89-44d0-9bfc-409f3b66dc57')
     def test_update_subnet_bumps_revision(self):
         net = self.create_network()
         subnet = self.create_subnet(net)
-        self.assertIn('revision', subnet)
+        self.assertIn('revision_number', subnet)
         updated = self.client.update_subnet(subnet['id'], name='newsub')
-        self.assertGreater(updated['subnet']['revision'], subnet['revision'])
+        self.assertGreater(updated['subnet']['revision_number'],
+                           subnet['revision_number'])
 
     @test.idempotent_id('e8c5d7db-2b8d-4615-a476-6e537437c4f2')
     def test_update_subnetpool_bumps_revision(self):
         sp = self.create_subnetpool('subnetpool', default_prefixlen=24,
                                     prefixes=['10.0.0.0/8'])
-        self.assertIn('revision', sp)
+        self.assertIn('revision_number', sp)
         updated = self.admin_client.update_subnetpool(sp['id'], name='sp2')
-        self.assertGreater(updated['subnetpool']['revision'], sp['revision'])
+        self.assertGreater(updated['subnetpool']['revision_number'],
+                           sp['revision_number'])
+
+    @test.idempotent_id('e8c5d7db-2b8d-4567-a326-6e123437c4d1')
+    def test_update_subnet_bumps_network_revision(self):
+        net = self.create_network()
+        subnet = self.create_subnet(net)
+        updated = self.client.show_network(net['id'])
+        self.assertGreater(updated['network']['revision_number'],
+                           net['revision_number'])
+        self.client.delete_subnet(subnet['id'])
+        updated2 = self.client.show_network(net['id'])
+        self.assertGreater(updated2['network']['revision_number'],
+                           updated['network']['revision_number'])
 
     @test.idempotent_id('6c256f71-c929-4200-b3dc-4e1843506be5')
     @test.requires_ext(extension="security-group", service="network")
     def test_update_sg_group_bumps_revision(self):
         sg, name = self._create_security_group()
-        self.assertIn('revision', sg['security_group'])
+        self.assertIn('revision_number', sg['security_group'])
         update_body = self.client.update_security_group(
             sg['security_group']['id'], name='new_sg_name')
-        self.assertGreater(update_body['security_group']['revision'],
-                           sg['security_group']['revision'])
+        self.assertGreater(update_body['security_group']['revision_number'],
+                           sg['security_group']['revision_number'])
 
     @test.idempotent_id('6489632f-8550-4453-a674-c98849742967')
     @test.requires_ext(extension="security-group", service="network")
@@ -74,15 +92,11 @@
         self.client.update_port(
             port['id'], security_groups=[sg['security_group']['id']])
         updated = self.client.show_port(port['id'])
-        self.client.update_port(port['id'], security_groups=[])
-        # TODO(kevinbenton): these extra shows after after the update are
-        # to work around the fact that ML2 creates the result dict before
-        # commit happens if the port is unbound. The update response should
-        # be usable directly once that is fixed.
-        updated2 = self.client.show_port(port['id'])
-        self.assertGreater(updated['port']['revision'], port['revision'])
-        self.assertGreater(updated2['port']['revision'],
-                           updated['port']['revision'])
+        updated2 = self.client.update_port(port['id'], security_groups=[])
+        self.assertGreater(updated['port']['revision_number'],
+                           port['revision_number'])
+        self.assertGreater(updated2['port']['revision_number'],
+                           updated['port']['revision_number'])
 
     @test.idempotent_id('29c7ab2b-d1d8-425d-8cec-fcf632960f22')
     @test.requires_ext(extension="security-group", service="network")
@@ -93,27 +107,138 @@
             protocol='tcp', direction='ingress', ethertype=self.ethertype,
             port_range_min=60, port_range_max=70)
         updated = self.client.show_security_group(sg['security_group']['id'])
-        self.assertGreater(updated['security_group']['revision'],
-                           sg['security_group']['revision'])
+        self.assertGreater(updated['security_group']['revision_number'],
+                           sg['security_group']['revision_number'])
         self.client.delete_security_group_rule(
             rule['security_group_rule']['id'])
         updated2 = self.client.show_security_group(sg['security_group']['id'])
-        self.assertGreater(updated2['security_group']['revision'],
-                           updated['security_group']['revision'])
+        self.assertGreater(updated2['security_group']['revision_number'],
+                           updated['security_group']['revision_number'])
+
+    @test.idempotent_id('db70c285-0365-4fac-9f55-2a0ad8cf55a8')
+    @test.requires_ext(extension="allowed-address-pairs", service="network")
+    def test_update_allowed_address_pairs_bumps_revision(self):
+        net = self.create_network()
+        port = self.create_port(net)
+        updated = self.client.update_port(
+            port['id'], allowed_address_pairs=[{'ip_address': '1.1.1.1/32'}])
+        self.assertGreater(updated['port']['revision_number'],
+                           port['revision_number'])
+        updated2 = self.client.update_port(
+            port['id'], allowed_address_pairs=[])
+        self.assertGreater(updated2['port']['revision_number'],
+                           updated['port']['revision_number'])
+
+    @test.idempotent_id('a21ec3b4-3569-4b77-bf29-4177edaa2df5')
+    @test.requires_ext(extension="extra_dhcp_opt", service="network")
+    def test_update_extra_dhcp_opt_bumps_revision(self):
+        net = self.create_network()
+        port = self.create_port(net)
+        opts = [{'opt_value': 'pxelinux.0', 'opt_name': 'bootfile-name'}]
+        updated = self.client.update_port(port['id'], extra_dhcp_opts=opts)
+        self.assertGreater(updated['port']['revision_number'],
+                           port['revision_number'])
+        opts[0]['opt_value'] = 'pxelinux.77'
+        updated2 = self.client.update_port(
+            port['id'], extra_dhcp_opts=opts)
+        self.assertGreater(updated2['port']['revision_number'],
+                           updated['port']['revision_number'])
+
+    @test.idempotent_id('40ba648f-f374-4c29-a5b7-489dd5a38a4e')
+    @test.requires_ext(extension="dns-integration", service="network")
+    def test_update_dns_domain_bumps_revision(self):
+        net = self.create_network(dns_domain='example.test.')
+        updated = self.client.update_network(net['id'], dns_domain='exa.test.')
+        self.assertGreater(updated['network']['revision_number'],
+                           net['revision_number'])
+        port = self.create_port(net)
+        updated = self.client.update_port(port['id'], dns_name='port1')
+        if not updated['port']['dns_name']:
+            self.skipTest("Server does not have DNS domain configured.")
+        self.assertGreater(updated['port']['revision_number'],
+                           port['revision_number'])
+        updated2 = self.client.update_port(port['id'], dns_name='')
+        self.assertGreater(updated2['port']['revision_number'],
+                           updated['port']['revision_number'])
+
+    @test.idempotent_id('8482324f-cf59-4d73-b98e-d37119255300')
+    @test.requires_ext(extension="router", service="network")
+    @test.requires_ext(extension="extraroute", service="network")
+    def test_update_router_extra_routes_bumps_revision(self):
+        subnet = self.create_subnet(self.create_network())
+        subgateway = netaddr.IPAddress(subnet['gateway_ip'])
+        router = self.create_router(router_name='test')
+        self.create_router_interface(router['id'], subnet['id'])
+        router = self.client.show_router(router['id'])['router']
+        updated = self.client.update_router(
+            router['id'], routes=[{'destination': '2.0.0.0/24',
+                                   'nexthop': str(subgateway + 1)}])
+        self.assertGreater(updated['router']['revision_number'],
+                           router['revision_number'])
+        updated2 = self.client.update_router(router['id'], routes=[])
+        self.assertGreater(updated2['router']['revision_number'],
+                           updated['router']['revision_number'])
+
+    @test.idempotent_id('6bd18702-e25a-4b4b-8c0c-680113533511')
+    @test.requires_ext(extension="subnet-service-types", service="network")
+    def test_update_subnet_service_types_bumps_revisions(self):
+        subnet = self.create_subnet(self.create_network())
+        updated = self.client.update_subnet(
+            subnet['id'], service_types=['compute:'])
+        self.assertGreater(updated['subnet']['revision_number'],
+                           subnet['revision_number'])
+        updated2 = self.client.update_subnet(
+            subnet['id'], service_types=[])
+        self.assertGreater(updated2['subnet']['revision_number'],
+                           updated['subnet']['revision_number'])
+
+    @test.idempotent_id('9c83105c-9973-45ff-9ca2-e66d64700abe')
+    @test.requires_ext(extension="port-security", service="network")
+    def test_update_port_security_bumps_revisions(self):
+        net = self.create_network(port_security_enabled=False)
+        updated = self.client.update_network(net['id'],
+                                             port_security_enabled=True)
+        self.assertGreater(updated['network']['revision_number'],
+                           net['revision_number'])
+        updated2 = self.client.update_network(net['id'],
+                                              port_security_enabled=False)
+        self.assertGreater(updated2['network']['revision_number'],
+                           updated['network']['revision_number'])
+        port = self.create_port(net, port_security_enabled=False)
+        updated = self.client.update_port(port['id'],
+                                          port_security_enabled=True)
+        self.assertGreater(updated['port']['revision_number'],
+                           port['revision_number'])
+        updated2 = self.client.update_port(port['id'],
+                                           port_security_enabled=False)
+        self.assertGreater(updated2['port']['revision_number'],
+                           updated['port']['revision_number'])
+
+    @test.idempotent_id('68d5ac3a-11a1-4847-8e2e-5843c043d89b')
+    @test.requires_ext(extension="binding", service="network")
+    def test_portbinding_bumps_revision(self):
+        port = self.create_port(self.create_network())
+        port = self.admin_client.update_port(
+            port['id'], **{'binding:host_id': 'badhost1'})['port']
+        updated = self.admin_client.update_port(
+            port['id'], **{'binding:host_id': 'badhost2'})['port']
+        self.assertGreater(updated['revision_number'],
+                           port['revision_number'])
 
     @test.idempotent_id('4a37bde9-1975-47e0-9b8c-2c9ca36415b0')
     @test.requires_ext(extension="router", service="network")
     def test_update_router_bumps_revision(self):
         subnet = self.create_subnet(self.create_network())
         router = self.create_router(router_name='test')
-        self.assertIn('revision', router)
-        rev1 = router['revision']
+        self.assertIn('revision_number', router)
+        rev1 = router['revision_number']
         router = self.client.update_router(router['id'],
                                            name='test2')['router']
-        self.assertGreater(router['revision'], rev1)
+        self.assertGreater(router['revision_number'], rev1)
         self.create_router_interface(router['id'], subnet['id'])
         updated = self.client.show_router(router['id'])['router']
-        self.assertGreater(updated['revision'], router['revision'])
+        self.assertGreater(updated['revision_number'],
+                           router['revision_number'])
 
     @test.idempotent_id('9de71ebc-f5df-4cd0-80bc-60299fce3ce9')
     @test.requires_ext(extension="router", service="network")
@@ -131,6 +256,9 @@
             port_id=port['id'],
             description='d1'
         )['floatingip']
-        self.assertIn('revision', body)
+        self.assertIn('revision_number', body)
         b2 = self.client.update_floatingip(body['id'], description='d2')
-        self.assertGreater(b2['floatingip']['revision'], body['revision'])
+        self.assertGreater(b2['floatingip']['revision_number'],
+                           body['revision_number'])
+        # disassociate
+        self.client.update_floatingip(b2['floatingip']['id'], port_id=None)
diff --git a/neutron/tests/tempest/api/test_security_groups_negative.py b/neutron/tests/tempest/api/test_security_groups_negative.py
index 68b5fd0..43bc88c 100644
--- a/neutron/tests/tempest/api/test_security_groups_negative.py
+++ b/neutron/tests/tempest/api/test_security_groups_negative.py
@@ -17,9 +17,6 @@
 from tempest import test
 
 from neutron.tests.tempest.api import base_security_groups as base
-from neutron.tests.tempest import config
-
-CONF = config.CONF
 
 
 class NegativeSecGroupTest(base.BaseSecGroupTest):
diff --git a/neutron/tests/tempest/api/test_subnetpools.py b/neutron/tests/tempest/api/test_subnetpools.py
index 5bd222f..e8ec954 100644
--- a/neutron/tests/tempest/api/test_subnetpools.py
+++ b/neutron/tests/tempest/api/test_subnetpools.py
@@ -110,13 +110,15 @@
         body = self._create_subnetpool(description='d1')
         self.assertEqual('d1', body['description'])
         sub_id = body['id']
-        body = filter(lambda x: x['id'] == sub_id,
-                      self.client.list_subnetpools()['subnetpools'])[0]
+        subnet_pools = [x for x in
+            self.client.list_subnetpools()['subnetpools'] if x['id'] == sub_id]
+        body = subnet_pools[0]
         self.assertEqual('d1', body['description'])
         body = self.client.update_subnetpool(sub_id, description='d2')
         self.assertEqual('d2', body['subnetpool']['description'])
-        body = filter(lambda x: x['id'] == sub_id,
-                      self.client.list_subnetpools()['subnetpools'])[0]
+        subnet_pools = [x for x in
+            self.client.list_subnetpools()['subnetpools'] if x['id'] == sub_id]
+        body = subnet_pools[0]
         self.assertEqual('d2', body['description'])
 
     @test.idempotent_id('741d08c2-1e3f-42be-99c7-0ea93c5b728c')
@@ -390,3 +392,7 @@
     @test.idempotent_id('82a13efc-c18f-4249-b8ec-cec7cf26fbd6')
     def test_list_no_pagination_limit_0(self):
         self._test_list_no_pagination_limit_0()
+
+    @test.idempotent_id('27feb3f8-40f4-4e50-8cd2-7d0096a98682')
+    def test_list_validation_filters(self):
+        self._test_list_validation_filters()
diff --git a/neutron/tests/tempest/api/test_subnets.py b/neutron/tests/tempest/api/test_subnets.py
index a3a2e00..4e63a3a 100644
--- a/neutron/tests/tempest/api/test_subnets.py
+++ b/neutron/tests/tempest/api/test_subnets.py
@@ -63,3 +63,7 @@
     @test.idempotent_id('d851937c-9821-4b46-9d18-43e9077ecac0')
     def test_list_no_pagination_limit_0(self):
         self._test_list_no_pagination_limit_0()
+
+    @test.idempotent_id('c0f9280b-9d81-4728-a967-6be22659d4c8')
+    def test_list_validation_filters(self):
+        self._test_list_validation_filters()
diff --git a/neutron/tests/tempest/api/test_timestamp.py b/neutron/tests/tempest/api/test_timestamp.py
index 6c36cf6..d3a2361 100644
--- a/neutron/tests/tempest/api/test_timestamp.py
+++ b/neutron/tests/tempest/api/test_timestamp.py
@@ -16,6 +16,11 @@
 from tempest import test
 
 from neutron.tests.tempest.api import base
+from neutron.tests.tempest.api import base_routers
+from neutron.tests.tempest.api import base_security_groups
+from neutron.tests.tempest import config
+
+CONF = config.CONF
 
 
 class TestTimeStamp(base.BaseAdminNetworkTest):
@@ -29,7 +34,7 @@
     larger_prefix = '10.11.0.0/16'
 
     @classmethod
-    @test.requires_ext(extension="timestamp_core", service="network")
+    @test.requires_ext(extension="standard-attr-timestamp", service="network")
     def skip_checks(cls):
         super(TestTimeStamp, cls).skip_checks()
 
@@ -174,3 +179,159 @@
         # verify the timestamp from creation and showed is same
         self.assertEqual(sp['created_at'], show_sp['created_at'])
         self.assertEqual(sp['updated_at'], show_sp['updated_at'])
+
+
+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")
+
+    @classmethod
+    def resource_setup(cls):
+        super(TestTimeStampWithL3, cls).resource_setup()
+        cls.ext_net_id = CONF.network.public_network_id
+
+    @test.idempotent_id('433ba770-b310-4da9-5d42-733217a1c7b1')
+    def test_create_router_with_timestamp(self):
+        router = self.create_router(router_name='test')
+        # Verifies body contains timestamp fields
+        self.assertIsNotNone(router['created_at'])
+        self.assertIsNotNone(router['updated_at'])
+
+    @test.idempotent_id('4a65417a-c11c-4b4d-a351-af01abcf57c6')
+    def test_update_router_with_timestamp(self):
+        router = self.create_router(router_name='test')
+        origin_updated_at = router['updated_at']
+        update_body = {'name': router['name'] + 'new'}
+        body = self.client.update_router(router['id'], **update_body)
+        updated_router = body['router']
+        new_updated_at = updated_router['updated_at']
+        self.assertEqual(router['created_at'], updated_router['created_at'])
+        # Verify that origin_updated_at is not same with new_updated_at
+        self.assertIsNot(origin_updated_at, new_updated_at)
+
+    @test.idempotent_id('1ab50ac2-7cbd-4a17-b23e-a9e36cfa4ec2')
+    def test_show_router_attribute_with_timestamp(self):
+        router = self.create_router(router_name='test')
+        body = self.client.show_router(router['id'])
+        show_router = body['router']
+        # verify the timestamp from creation and showed is same
+        self.assertEqual(router['created_at'],
+                         show_router['created_at'])
+        self.assertEqual(router['updated_at'],
+                         show_router['updated_at'])
+
+    @test.idempotent_id('8ae55186-464f-4b87-1c9f-eb2765ee81ac')
+    def test_create_floatingip_with_timestamp(self):
+        fip = self.create_floatingip(self.ext_net_id)
+        # Verifies body contains timestamp fields
+        self.assertIsNotNone(fip['created_at'])
+        self.assertIsNotNone(fip['updated_at'])
+
+    @test.idempotent_id('a3ac215a-61ac-13f9-9d3c-57c51f11afa1')
+    def test_update_floatingip_with_timestamp(self):
+        fip = self.create_floatingip(self.ext_net_id)
+        origin_updated_at = fip['updated_at']
+        update_body = {'description': 'new'}
+        body = self.client.update_floatingip(fip['id'], **update_body)
+        updated_fip = body['floatingip']
+        new_updated_at = updated_fip['updated_at']
+        self.assertEqual(fip['created_at'], updated_fip['created_at'])
+        # Verify that origin_updated_at is not same with new_updated_at
+        self.assertIsNot(origin_updated_at, new_updated_at)
+
+    @test.idempotent_id('32a6a086-e1ef-413b-b13a-0cfe13ef051e')
+    def test_show_floatingip_attribute_with_timestamp(self):
+        fip = self.create_floatingip(self.ext_net_id)
+        body = self.client.show_floatingip(fip['id'])
+        show_fip = body['floatingip']
+        # verify the timestamp from creation and showed is same
+        self.assertEqual(fip['created_at'],
+                         show_fip['created_at'])
+        self.assertEqual(fip['updated_at'],
+                         show_fip['updated_at'])
+
+
+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")
+
+    @classmethod
+    def resource_setup(cls):
+        super(TestTimeStampWithSecurityGroup, cls).resource_setup()
+        cls.ext_net_id = CONF.network.public_network_id
+
+    @test.idempotent_id('a3150a7b-d31a-423a-abf3-45e71c97cbac')
+    def test_create_sg_with_timestamp(self):
+        sg, _ = self._create_security_group()
+        # Verifies body contains timestamp fields
+        self.assertIsNotNone(sg['security_group']['created_at'])
+        self.assertIsNotNone(sg['security_group']['updated_at'])
+
+    @test.idempotent_id('432ae0d3-32b4-413e-a9b3-091ac76da31b')
+    def test_update_sg_with_timestamp(self):
+        sgc, _ = self._create_security_group()
+        sg = sgc['security_group']
+        origin_updated_at = sg['updated_at']
+        update_body = {'name': sg['name'] + 'new'}
+        body = self.client.update_security_group(sg['id'], **update_body)
+        updated_sg = body['security_group']
+        new_updated_at = updated_sg['updated_at']
+        self.assertEqual(sg['created_at'], updated_sg['created_at'])
+        # Verify that origin_updated_at is not same with new_updated_at
+        self.assertIsNot(origin_updated_at, new_updated_at)
+
+    @test.idempotent_id('521e6723-43d6-12a6-8c3d-f5042ad9fc32')
+    def test_show_sg_attribute_with_timestamp(self):
+        sg, _ = self._create_security_group()
+        body = self.client.show_security_group(sg['security_group']['id'])
+        show_sg = body['security_group']
+        # verify the timestamp from creation and showed is same
+        self.assertEqual(sg['security_group']['created_at'],
+                         show_sg['created_at'])
+        self.assertEqual(sg['security_group']['updated_at'],
+                         show_sg['updated_at'])
+
+    def _prepare_sgrule_test(self):
+        sg, _ = self._create_security_group()
+        sg_id = sg['security_group']['id']
+        direction = 'ingress'
+        protocol = 'tcp'
+        port_range_min = 77
+        port_range_max = 77
+        rule_create_body = self.client.create_security_group_rule(
+            security_group_id=sg_id,
+            direction=direction,
+            ethertype=self.ethertype,
+            protocol=protocol,
+            port_range_min=port_range_min,
+            port_range_max=port_range_max,
+            remote_group_id=None,
+            remote_ip_prefix=None
+        )
+        return rule_create_body['security_group_rule']
+
+    @test.idempotent_id('83e8bd32-43e0-a3f0-1af3-12a5733c653e')
+    def test_create_sgrule_with_timestamp(self):
+        sgrule = self._prepare_sgrule_test()
+        # Verifies body contains timestamp fields
+        self.assertIsNotNone(sgrule['created_at'])
+        self.assertIsNotNone(sgrule['updated_at'])
+
+    @test.idempotent_id('143da0e6-ba17-43ad-b3d7-03aa759c3cb4')
+    def test_show_sgrule_attribute_with_timestamp(self):
+        sgrule = self._prepare_sgrule_test()
+        body = self.client.show_security_group_rule(sgrule['id'])
+        show_sgrule = body['security_group_rule']
+        # verify the timestamp from creation and showed is same
+        self.assertEqual(sgrule['created_at'], show_sgrule['created_at'])
+        self.assertEqual(sgrule['updated_at'], show_sgrule['updated_at'])
diff --git a/neutron/tests/tempest/api/test_trunk.py b/neutron/tests/tempest/api/test_trunk.py
index c22c334..3dc8f0d 100644
--- a/neutron/tests/tempest/api/test_trunk.py
+++ b/neutron/tests/tempest/api/test_trunk.py
@@ -12,11 +12,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 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
 
 
 def trunks_cleanup(client, trunks):
@@ -65,53 +67,73 @@
         self.trunks.append(trunk['trunk'])
         return trunk
 
+    def _show_trunk(self, trunk_id):
+        return self.client.show_trunk(trunk_id)
+
+    def _list_trunks(self):
+        return self.client.list_trunks()
+
 
 class TrunkTestJSON(TrunkTestJSONBase):
 
+    def _test_create_trunk(self, subports):
+        trunk = self._create_trunk_with_network_and_parent(subports)
+        observed_trunk = self._show_trunk(trunk['trunk']['id'])
+        self.assertEqual(trunk, observed_trunk)
+
     @test.idempotent_id('e1a6355c-4768-41f3-9bf8-0f1d192bd501')
     def test_create_trunk_empty_subports_list(self):
-        trunk = self._create_trunk_with_network_and_parent([])
-        observed_trunk = self.client.show_trunk(trunk['trunk']['id'])
-        self.assertEqual(trunk, observed_trunk)
+        self._test_create_trunk([])
 
     @test.idempotent_id('382dfa39-ca03-4bd3-9a1c-91e36d2e3796')
     def test_create_trunk_subports_not_specified(self):
-        trunk = self._create_trunk_with_network_and_parent(None)
-        observed_trunk = self.client.show_trunk(trunk['trunk']['id'])
-        self.assertEqual(trunk, observed_trunk)
+        self._test_create_trunk(None)
 
     @test.idempotent_id('7de46c22-e2b6-4959-ac5a-0e624632ab32')
     def test_create_show_delete_trunk(self):
         trunk = self._create_trunk_with_network_and_parent(None)
         trunk_id = trunk['trunk']['id']
         parent_port_id = trunk['trunk']['port_id']
-        res = self.client.show_trunk(trunk_id)
+        res = self._show_trunk(trunk_id)
         self.assertEqual(trunk_id, res['trunk']['id'])
         self.assertEqual(parent_port_id, res['trunk']['port_id'])
         self.client.delete_trunk(trunk_id)
-        self.assertRaises(lib_exc.NotFound, self.client.show_trunk, trunk_id)
+        self.assertRaises(lib_exc.NotFound, self._show_trunk, trunk_id)
 
     @test.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'])
         trunk_id = trunk['trunk']['id']
-        res = self.client.show_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("", 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)
         # enable the trunk so that it can be managed
         self.client.update_trunk(trunk_id, admin_state_up=True)
 
+    @test.idempotent_id('5ff46c22-a2b6-5559-bc5a-0ef2463cab32')
+    def test_create_update_trunk_with_description(self):
+        trunk = self._create_trunk_with_network_and_parent(
+            None, description="foo description")
+        trunk_id = trunk['trunk']['id']
+        self.assertEqual("foo description", trunk['trunk']['description'])
+        trunk = self.client.update_trunk(trunk_id, description='')
+        self.assertEqual('', trunk['trunk']['description'])
+
     @test.idempotent_id('73365f73-bed6-42cd-960b-ec04e0c99d85')
     def test_list_trunks(self):
         trunk1 = self._create_trunk_with_network_and_parent(None)
         trunk2 = self._create_trunk_with_network_and_parent(None)
         expected_trunks = {trunk1['trunk']['id']: trunk1['trunk'],
                            trunk2['trunk']['id']: trunk2['trunk']}
-        trunk_list = self.client.list_trunks()['trunks']
+        trunk_list = self._list_trunks()['trunks']
         matched_trunks = [x for x in trunk_list if x['id'] in expected_trunks]
         self.assertEqual(2, len(matched_trunks))
         for trunk in matched_trunks:
@@ -126,7 +148,7 @@
                      'segmentation_type': 'vlan',
                      'segmentation_id': 2}]
         self.client.add_subports(trunk['trunk']['id'], subports)
-        trunk = self.client.show_trunk(trunk['trunk']['id'])
+        trunk = self._show_trunk(trunk['trunk']['id'])
         observed_subports = trunk['trunk']['sub_ports']
         self.assertEqual(1, len(observed_subports))
         created_subport = observed_subports[0]
@@ -168,7 +190,7 @@
         self.assertEqual(expected_subport, res['sub_ports'][0])
 
         # Validate the results of a subport list
-        trunk = self.client.show_trunk(trunk['trunk']['id'])
+        trunk = self._show_trunk(trunk['trunk']['id'])
         observed_subports = trunk['trunk']['sub_ports']
         self.assertEqual(1, len(observed_subports))
         self.assertEqual(expected_subport, observed_subports[0])
@@ -186,6 +208,85 @@
         self.assertEqual(1, len(observed_subports))
 
 
+class TrunkTestMtusJSONBase(TrunkTestJSONBase):
+
+    required_extensions = ['provider', 'trunk']
+
+    @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']):
+            msg = "Either vxlan or gre type driver not enabled."
+            raise cls.skipException(msg)
+
+    def setUp(self):
+        super(TrunkTestMtusJSONBase, self).setUp()
+
+        # VXLAN autocomputed MTU (1450) is smaller than that of GRE (1458)
+        vxlan_kwargs = {'network_name': data_utils.rand_name('vxlan-net-'),
+                        'provider:network_type': 'vxlan'}
+        self.smaller_mtu_net = self.create_shared_network(**vxlan_kwargs)
+
+        gre_kwargs = {'network_name': data_utils.rand_name('gre-net-'),
+                      'provider:network_type': 'gre'}
+        self.larger_mtu_net = self.create_shared_network(**gre_kwargs)
+
+        self.smaller_mtu_port = self.create_port(self.smaller_mtu_net)
+        self.smaller_mtu_port_2 = self.create_port(self.smaller_mtu_net)
+        self.larger_mtu_port = self.create_port(self.larger_mtu_net)
+
+
+class TrunkTestMtusJSON(TrunkTestMtusJSONBase):
+
+    @test.idempotent_id('0f05d98e-41f5-4629-ac29-9aee269c9602')
+    def test_create_trunk_with_mtu_greater_than_subport(self):
+        subports = [{'port_id': self.smaller_mtu_port['id'],
+                     'segmentation_type': 'vlan',
+                     'segmentation_id': 2}]
+
+        trunk = self.client.create_trunk(self.larger_mtu_port['id'], subports)
+        self.trunks.append(trunk['trunk'])
+
+    @test.idempotent_id('2004c5c6-e557-4c43-8100-c820ad4953e8')
+    def test_add_subport_with_mtu_smaller_than_trunk(self):
+        subports = [{'port_id': self.smaller_mtu_port['id'],
+                     'segmentation_type': 'vlan',
+                     'segmentation_id': 2}]
+
+        trunk = self.client.create_trunk(self.larger_mtu_port['id'], None)
+        self.trunks.append(trunk['trunk'])
+
+        self.client.add_subports(trunk['trunk']['id'], subports)
+
+    @test.idempotent_id('22725101-f4bc-4e00-84ec-4e02cd7e0500')
+    def test_create_trunk_with_mtu_equal_to_subport(self):
+        subports = [{'port_id': self.smaller_mtu_port['id'],
+                     'segmentation_type': 'vlan',
+                     'segmentation_id': 2}]
+
+        trunk = self.client.create_trunk(self.smaller_mtu_port_2['id'],
+                                         subports)
+        self.trunks.append(trunk['trunk'])
+
+    @test.idempotent_id('175b05ae-66ad-44c7-857a-a12d16f1058f')
+    def test_add_subport_with_mtu_equal_to_trunk(self):
+        subports = [{'port_id': self.smaller_mtu_port['id'],
+                     'segmentation_type': 'vlan',
+                     'segmentation_id': 2}]
+
+        trunk = self.client.create_trunk(self.smaller_mtu_port_2['id'], None)
+        self.trunks.append(trunk['trunk'])
+
+        self.client.add_subports(trunk['trunk']['id'], subports)
+
+
 class TrunksSearchCriteriaTest(base.BaseSearchCriteriaTest):
 
     resource = 'trunk'
diff --git a/neutron/tests/tempest/api/test_trunk_details.py b/neutron/tests/tempest/api/test_trunk_details.py
index 4b7ec28..0c91c45 100644
--- a/neutron/tests/tempest/api/test_trunk_details.py
+++ b/neutron/tests/tempest/api/test_trunk_details.py
@@ -35,15 +35,16 @@
     @test.idempotent_id('544bcaf2-86fb-4930-93ab-ece1c3cc33df')
     def test_port_resource_trunk_details_with_subport(self):
         subport_network = self.create_network()
-        parent_port = self.create_port(subport_network)
-        subport_data = {'port_id': parent_port['id'],
+        subport = self.create_port(subport_network)
+        subport_data = {'port_id': subport['id'],
                         'segmentation_type': 'vlan',
                         'segmentation_id': 2}
         trunk = self._create_trunk_with_network_and_parent([subport_data])
-        port = self.client.show_port(trunk['trunk']['port_id'])
+        subport_data['mac_address'] = subport['mac_address']
+        parent_port = self.client.show_port(trunk['trunk']['port_id'])
         expected_trunk_details = {'sub_ports': [subport_data],
                                   'trunk_id': trunk['trunk']['id']}
-        observed_trunk_details = port['port'].get('trunk_details')
+        observed_trunk_details = parent_port['port'].get('trunk_details')
         self.assertIsNotNone(observed_trunk_details)
         self.assertEqual(expected_trunk_details,
                          observed_trunk_details)
diff --git a/neutron/tests/tempest/api/test_trunk_negative.py b/neutron/tests/tempest/api/test_trunk_negative.py
index 654b497..b26dd75 100644
--- a/neutron/tests/tempest/api/test_trunk_negative.py
+++ b/neutron/tests/tempest/api/test_trunk_negative.py
@@ -15,6 +15,7 @@
 from oslo_utils import uuidutils
 from tempest.lib import exceptions as lib_exc
 from tempest import test
+import testtools
 
 from neutron.tests.tempest.api import test_trunk
 
@@ -235,3 +236,35 @@
         self._create_trunk_with_network_and_parent(subports)
         self.assertRaises(lib_exc.Conflict, self.client.delete_port,
                           port['id'])
+
+
+class TrunkTestMtusJSON(test_trunk.TrunkTestMtusJSONBase):
+
+    required_extensions = (
+        ['net-mtu'] + test_trunk.TrunkTestMtusJSONBase.required_extensions)
+
+    @test.attr(type='negative')
+    @test.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'],
+                     'segmentation_type': 'vlan',
+                     'segmentation_id': 2}]
+
+        with testtools.ExpectedException(lib_exc.Conflict):
+            trunk = self.client.create_trunk(self.smaller_mtu_port['id'],
+                                             subports)
+            self.trunks.append(trunk['trunk'])
+
+    @test.attr(type='negative')
+    @test.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'],
+                     'segmentation_type': 'vlan',
+                     'segmentation_id': 2}]
+
+        trunk = self.client.create_trunk(self.smaller_mtu_port['id'], None)
+        self.trunks.append(trunk['trunk'])
+
+        self.assertRaises(lib_exc.Conflict,
+                          self.client.add_subports,
+                          trunk['trunk']['id'], subports)
diff --git a/neutron/tests/tempest/config.py b/neutron/tests/tempest/config.py
index f50fa39..479d909 100644
--- a/neutron/tests/tempest/config.py
+++ b/neutron/tests/tempest/config.py
@@ -22,7 +22,12 @@
     cfg.BoolOpt('specify_floating_ip_address_available',
                 default=True,
                 help='Allow passing an IP Address of the floating ip when '
-                     'creating the floating ip')]
+                     'creating the floating ip'),
+    cfg.ListOpt('available_type_drivers',
+                default=[],
+                help='List of network types available to neutron, '
+                     'e.g. vxlan,vlan,gre.'),
+]
 
 # TODO(amuller): Redo configuration options registration as part of the planned
 # transition to the Tempest plugin architecture
diff --git a/neutron/tests/tempest/scenario/test_trunk.py b/neutron/tests/tempest/scenario/test_trunk.py
new file mode 100644
index 0000000..30d6022
--- /dev/null
+++ b/neutron/tests/tempest/scenario/test_trunk.py
@@ -0,0 +1,160 @@
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_log import log as logging
+from tempest.common import waiters
+from tempest import test
+
+from neutron.common import utils
+from neutron.tests.tempest import config
+from neutron.tests.tempest.scenario import base
+from neutron.tests.tempest.scenario import constants
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+class TrunkTest(base.BaseTempestTestCase):
+    credentials = ['primary']
+    force_tenant_isolation = False
+
+    @classmethod
+    @test.requires_ext(extension="trunk", service="network")
+    def resource_setup(cls):
+        super(TrunkTest, cls).resource_setup()
+        # setup basic topology for servers we can log into
+        cls.network = cls.create_network()
+        cls.subnet = cls.create_subnet(cls.network)
+        cls.create_router_and_interface(cls.subnet['id'])
+        cls.keypair = cls.create_keypair()
+        cls.create_loginable_secgroup_rule()
+
+    def _create_server_with_trunk_port(self):
+        port = self.create_port(self.network)
+        trunk = self.client.create_trunk(port['id'], subports=[])['trunk']
+        fip = self.create_and_associate_floatingip(port['id'])
+        server = self.create_server(
+            flavor_ref=CONF.compute.flavor_ref,
+            image_ref=CONF.compute.image_ref,
+            key_name=self.keypair['name'],
+            networks=[{'port': port['id']}])['server']
+        self.addCleanup(self._detach_and_delete_trunk, server, trunk)
+        return {'port': port, 'trunk': trunk, 'fip': fip,
+                'server': server}
+
+    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(
+            server['id'], trunk['port_id'])
+
+        def is_port_detached():
+            p = self.client.show_port(trunk['port_id'])['port']
+            return p['device_id'] == ''
+        utils.wait_until_true(is_port_detached)
+        self.client.delete_trunk(trunk['id'])
+
+    def _is_port_down(self, port_id):
+        p = self.client.show_port(port_id)['port']
+        return p['status'] == 'DOWN'
+
+    def _is_port_active(self, port_id):
+        p = self.client.show_port(port_id)['port']
+        return p['status'] == 'ACTIVE'
+
+    def _is_trunk_active(self, trunk_id):
+        t = self.client.show_trunk(trunk_id)['trunk']
+        return t['status'] == 'ACTIVE'
+
+    @test.idempotent_id('bb13fe28-f152-4000-8131-37890a40c79e')
+    def test_trunk_subport_lifecycle(self):
+        """Test trunk creation and subport transition to ACTIVE status.
+
+        This is a basic test for the trunk extension to ensure that we
+        can create a trunk, attach it to a server, add/remove subports,
+        while ensuring the status transitions as appropriate.
+
+        This test does not assert any dataplane behavior for the subports.
+        It's just a high-level check to ensure the agents claim to have
+        wired the port correctly and that the trunk port itself maintains
+        connectivity.
+        """
+        server1 = self._create_server_with_trunk_port()
+        server2 = self._create_server_with_trunk_port()
+        for server in (server1, server2):
+            waiters.wait_for_server_status(self.manager.servers_client,
+                                           server['server']['id'],
+                                           constants.SERVER_STATUS_ACTIVE)
+            self.check_connectivity(server['fip']['floating_ip_address'],
+                                    CONF.validation.image_ssh_user,
+                                    self.keypair['private_key'])
+        trunk1_id, trunk2_id = server1['trunk']['id'], server2['trunk']['id']
+        # trunks should transition to ACTIVE without any subports
+        utils.wait_until_true(
+            lambda: self._is_trunk_active(trunk1_id),
+            exception=RuntimeError("Timed out waiting for trunk %s to "
+                                   "transition to ACTIVE." % trunk1_id))
+        utils.wait_until_true(
+            lambda: self._is_trunk_active(trunk2_id),
+            exception=RuntimeError("Timed out waiting for trunk %s to "
+                                   "transition to ACTIVE." % trunk2_id))
+        # create a few more networks and ports for subports
+        subports = [{'port_id': self.create_port(self.create_network())['id'],
+                     'segmentation_type': 'vlan', 'segmentation_id': seg_id}
+                    for seg_id in range(3, 7)]
+        # add all subports to server1
+        self.client.add_subports(trunk1_id, subports)
+        # ensure trunk transitions to ACTIVE
+        utils.wait_until_true(
+            lambda: self._is_trunk_active(trunk1_id),
+            exception=RuntimeError("Timed out waiting for trunk %s to "
+                                   "transition to ACTIVE." % trunk1_id))
+        # ensure all underlying subports transitioned to ACTIVE
+        for s in subports:
+            utils.wait_until_true(lambda: self._is_port_active(s['port_id']))
+        # ensure main dataplane wasn't interrupted
+        self.check_connectivity(server1['fip']['floating_ip_address'],
+                                CONF.validation.image_ssh_user,
+                                self.keypair['private_key'])
+        # move subports over to other server
+        self.client.remove_subports(trunk1_id, subports)
+        # ensure all subports go down
+        for s in subports:
+            utils.wait_until_true(
+                lambda: self._is_port_down(s['port_id']),
+                exception=RuntimeError("Timed out waiting for subport %s to "
+                                       "transition to DOWN." % s['port_id']))
+        self.client.add_subports(trunk2_id, subports)
+        # wait for both trunks to go back to ACTIVE
+        utils.wait_until_true(
+            lambda: self._is_trunk_active(trunk1_id),
+            exception=RuntimeError("Timed out waiting for trunk %s to "
+                                   "transition to ACTIVE." % trunk1_id))
+        utils.wait_until_true(
+            lambda: self._is_trunk_active(trunk2_id),
+            exception=RuntimeError("Timed out waiting for trunk %s to "
+                                   "transition to ACTIVE." % trunk2_id))
+        # ensure subports come up on other trunk
+        for s in subports:
+            utils.wait_until_true(
+                lambda: self._is_port_active(s['port_id']),
+                exception=RuntimeError("Timed out waiting for subport %s to "
+                                       "transition to ACTIVE." % s['port_id']))
+        # final connectivity check
+        self.check_connectivity(server1['fip']['floating_ip_address'],
+                                CONF.validation.image_ssh_user,
+                                self.keypair['private_key'])
+        self.check_connectivity(server2['fip']['floating_ip_address'],
+                                CONF.validation.image_ssh_user,
+                                self.keypair['private_key'])
diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py
index 9c8cb05..3b1a9a8 100644
--- a/neutron/tests/tempest/services/network/json/network_client.py
+++ b/neutron/tests/tempest/services/network/json/network_client.py
@@ -55,6 +55,7 @@
             'metering_label_rules': 'metering',
             'policies': 'qos',
             'bandwidth_limit_rules': 'qos',
+            'minimum_bandwidth_rules': 'qos',
             'rule_types': 'qos',
             'rbac-policies': '',
         }
@@ -652,6 +653,53 @@
         self.expected_success(204, resp.status)
         return service_client.ResponseBody(resp, body)
 
+    def create_minimum_bandwidth_rule(self, policy_id, direction,
+                                      min_kbps=None):
+        uri = '%s/qos/policies/%s/minimum_bandwidth_rules' % (
+            self.uri_prefix, policy_id)
+        data = {
+            'direction': direction,
+        }
+        if min_kbps is not None:
+            data['min_kbps'] = min_kbps
+        post_data = self.serialize({'minimum_bandwidth_rule': data})
+        resp, body = self.post(uri, post_data)
+        self.expected_success(201, resp.status)
+        body = jsonutils.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def list_minimum_bandwidth_rules(self, policy_id):
+        uri = '%s/qos/policies/%s/minimum_bandwidth_rules' % (
+            self.uri_prefix, policy_id)
+        resp, body = self.get(uri)
+        body = self.deserialize_single(body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def show_minimum_bandwidth_rule(self, policy_id, rule_id):
+        uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+            self.uri_prefix, policy_id, rule_id)
+        resp, body = self.get(uri)
+        body = self.deserialize_single(body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def update_minimum_bandwidth_rule(self, policy_id, rule_id, **kwargs):
+        uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+            self.uri_prefix, policy_id, rule_id)
+        post_data = {'minimum_bandwidth_rule': kwargs}
+        resp, body = self.put(uri, jsonutils.dumps(post_data))
+        body = self.deserialize_single(body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def delete_minimum_bandwidth_rule(self, policy_id, rule_id):
+        uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+            self.uri_prefix, policy_id, rule_id)
+        resp, body = self.delete(uri)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
     def list_qos_rule_types(self):
         uri = '%s/qos/rule-types' % self.uri_prefix
         resp, body = self.get(uri)
@@ -660,7 +708,8 @@
         return service_client.ResponseBody(resp, body)
 
     def create_trunk(self, parent_port_id, subports,
-                     tenant_id=None, name=None, admin_state_up=None):
+                     tenant_id=None, name=None, admin_state_up=None,
+                     description=None):
         uri = '%s/trunks' % self.uri_prefix
         post_data = {
             'trunk': {
@@ -673,6 +722,8 @@
             post_data['trunk']['tenant_id'] = tenant_id
         if name is not None:
             post_data['trunk']['name'] = name
+        if description is not None:
+            post_data['trunk']['description'] = description
         if admin_state_up is not None:
             post_data['trunk']['admin_state_up'] = admin_state_up
         resp, body = self.post(uri, self.serialize(post_data))
@@ -738,6 +789,35 @@
         body = jsonutils.loads(body)
         return service_client.ResponseBody(resp, body)
 
+    def delete_auto_allocated_topology(self, tenant_id=None):
+        uri = '%s/auto-allocated-topology/%s' % (self.uri_prefix, tenant_id)
+        resp, body = self.delete(uri)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def create_flavor_service_profile(self, flavor_id, service_profile_id):
+        body = jsonutils.dumps({'service_profile': {'id': service_profile_id}})
+        uri = '%s/flavors/%s/service_profiles' % (self.uri_prefix, flavor_id)
+        resp, body = self.post(uri, body)
+        self.expected_success(201, resp.status)
+        body = jsonutils.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def list_flavor_service_profiles(self, flavor_id):
+        uri = '%s/flavors/%s/service_profiles' % (self.uri_prefix, flavor_id)
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def delete_flavor_service_profile(self, flavor_id, service_profile_id):
+        uri = '%s/flavors/%s/service_profiles/%s' % (self.uri_prefix,
+                                                     flavor_id,
+                                                     service_profile_id)
+        resp, body = self.delete(uri)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
     def create_security_group_rule(self, direction, security_group_id,
                                    **kwargs):
         post_body = {'security_group_rule': kwargs}
@@ -792,6 +872,21 @@
         body = jsonutils.loads(body)
         return service_client.ResponseBody(resp, body)
 
+    def create_network_keystone_v3(self, name, project_id, tenant_id=None):
+        uri = '%s/networks' % self.uri_prefix
+        post_data = {
+            'network': {
+                'name': name,
+                'project_id': project_id
+            }
+        }
+        if tenant_id is not None:
+            post_data['network']['tenant_id'] = tenant_id
+        resp, body = self.post(uri, self.serialize(post_data))
+        body = self.deserialize_single(body)
+        self.expected_success(201, resp.status)
+        return service_client.ResponseBody(resp, body)
+
     def list_extensions(self, **filters):
         uri = self.get_uri("extensions")
         if filters: