Merge "Fix for creation of network environment twice"
diff --git a/neutron/tests/tempest/api/admin/test_dhcp_agent_scheduler.py b/neutron/tests/tempest/api/admin/test_dhcp_agent_scheduler.py
index d058ffd..5576e0d 100644
--- a/neutron/tests/tempest/api/admin/test_dhcp_agent_scheduler.py
+++ b/neutron/tests/tempest/api/admin/test_dhcp_agent_scheduler.py
@@ -12,8 +12,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from neutron_lib import constants
 from tempest import test
 
+from neutron.common import utils
 from neutron.tests.tempest.api import base
 
 
@@ -30,6 +32,18 @@
         cls.cidr = cls.subnet['cidr']
         cls.port = cls.create_port(cls.network)
 
+    @test.idempotent_id('f164801e-1dd8-4b8b-b5d3-cc3ac77cfaa5')
+    def test_dhcp_port_status_active(self):
+
+        def dhcp_port_active():
+            for p in self.client.list_ports(
+                    network_id=self.network['id'])['ports']:
+                if (p['device_owner'] == constants.DEVICE_OWNER_DHCP and
+                        p['status'] == constants.PORT_STATUS_ACTIVE):
+                    return True
+            return False
+        utils.wait_until_true(dhcp_port_active)
+
     @test.idempotent_id('5032b1fe-eb42-4a64-8f3b-6e189d8b5c7d')
     def test_list_dhcp_agent_hosting_network(self):
         self.admin_client.list_dhcp_agent_hosting_network(
diff --git a/neutron/tests/tempest/api/test_revisions.py b/neutron/tests/tempest/api/test_revisions.py
new file mode 100644
index 0000000..92f7866
--- /dev/null
+++ b/neutron/tests/tempest/api/test_revisions.py
@@ -0,0 +1,136 @@
+#    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 import test
+
+from neutron.tests.tempest.api import base
+from neutron.tests.tempest.api import base_security_groups as bsg
+from neutron.tests.tempest import config
+
+
+class TestRevisions(base.BaseAdminNetworkTest, bsg.BaseSecGroupTest):
+
+    @classmethod
+    @test.requires_ext(extension="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)
+        updated = self.client.update_network(net['id'], name='newnet')
+        self.assertGreater(updated['network']['revision'], net['revision'])
+
+    @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)
+        updated = self.client.update_port(port['id'], name='newport')
+        self.assertGreater(updated['port']['revision'], port['revision'])
+
+    @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)
+        updated = self.client.update_subnet(subnet['id'], name='newsub')
+        self.assertGreater(updated['subnet']['revision'], subnet['revision'])
+
+    @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)
+        updated = self.admin_client.update_subnetpool(sp['id'], name='sp2')
+        self.assertGreater(updated['subnetpool']['revision'], sp['revision'])
+
+    @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'])
+        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'])
+
+    @test.idempotent_id('6489632f-8550-4453-a674-c98849742967')
+    @test.requires_ext(extension="security-group", service="network")
+    def test_update_port_sg_binding_bumps_revision(self):
+        net = self.create_network()
+        port = self.create_port(net)
+        sg = self._create_security_group()[0]
+        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'])
+
+    @test.idempotent_id('29c7ab2b-d1d8-425d-8cec-fcf632960f22')
+    @test.requires_ext(extension="security-group", service="network")
+    def test_update_sg_rule_bumps_sg_revision(self):
+        sg, name = self._create_security_group()
+        rule = self.client.create_security_group_rule(
+            security_group_id=sg['security_group']['id'],
+            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.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'])
+
+    @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']
+        router = self.client.update_router(router['id'],
+                                           name='test2')['router']
+        self.assertGreater(router['revision'], rev1)
+        self.create_router_interface(router['id'], subnet['id'])
+        updated = self.client.show_router(router['id'])['router']
+        self.assertGreater(updated['revision'], router['revision'])
+
+    @test.idempotent_id('9de71ebc-f5df-4cd0-80bc-60299fce3ce9')
+    @test.requires_ext(extension="router", service="network")
+    @test.requires_ext(extension="standard-attr-description",
+                       service="network")
+    def test_update_floatingip_bumps_revision(self):
+        ext_id = config.CONF.network.public_network_id
+        network = self.create_network()
+        subnet = self.create_subnet(network)
+        router = self.create_router('test', external_network_id=ext_id)
+        self.create_router_interface(router['id'], subnet['id'])
+        port = self.create_port(network)
+        body = self.client.create_floatingip(
+            floating_network_id=ext_id,
+            port_id=port['id'],
+            description='d1'
+        )['floatingip']
+        self.assertIn('revision', body)
+        b2 = self.client.update_floatingip(body['id'], description='d2')
+        self.assertGreater(b2['floatingip']['revision'], body['revision'])
diff --git a/neutron/tests/tempest/api/test_routers.py b/neutron/tests/tempest/api/test_routers.py
index 90e13ed..20e54cf 100644
--- a/neutron/tests/tempest/api/test_routers.py
+++ b/neutron/tests/tempest/api/test_routers.py
@@ -18,6 +18,7 @@
 from tempest.lib.common.utils import data_utils
 from tempest import test
 
+from neutron.common import utils
 from neutron.tests.tempest.api import base
 from neutron.tests.tempest.api import base_routers
 from neutron.tests.tempest import config
@@ -153,6 +154,17 @@
              'enable_snat': False})
         self._verify_gateway_port(router['id'])
 
+    @test.idempotent_id('db3093b1-93b6-4893-be83-c4716c251b3e')
+    def test_router_interface_status(self):
+        network = self.create_network()
+        subnet = self.create_subnet(network)
+        # Add router interface with subnet id
+        router = self._create_router(data_utils.rand_name('router-'), True)
+        intf = self.create_router_interface(router['id'], subnet['id'])
+        status_active = lambda: self.client.show_port(
+            intf['port_id'])['port']['status'] == 'ACTIVE'
+        utils.wait_until_true(status_active)
+
     @test.idempotent_id('c86ac3a8-50bd-4b00-a6b8-62af84a0765c')
     @test.requires_ext(extension='extraroute', service='network')
     def test_update_extra_route(self):
diff --git a/neutron/tests/tempest/api/test_routers_negative.py b/neutron/tests/tempest/api/test_routers_negative.py
index 6a028db..309b6fd 100644
--- a/neutron/tests/tempest/api/test_routers_negative.py
+++ b/neutron/tests/tempest/api/test_routers_negative.py
@@ -24,6 +24,11 @@
 class RoutersNegativeTestBase(base.BaseRouterTest):
 
     @classmethod
+    @test.requires_ext(extension="router", service="network")
+    def skip_checks(cls):
+        super(RoutersNegativeTestBase, cls).skip_checks()
+
+    @classmethod
     def resource_setup(cls):
         super(RoutersNegativeTestBase, cls).resource_setup()
         cls.router = cls.create_router(data_utils.rand_name('router'))
@@ -33,11 +38,6 @@
 
 class RoutersNegativeTest(RoutersNegativeTestBase):
 
-    @classmethod
-    @test.requires_ext(extension="router", service="network")
-    def skip_checks(cls):
-        super(RoutersNegativeTest, cls).skip_checks()
-
     @test.attr(type='negative')
     @test.idempotent_id('e3e751af-15a2-49cc-b214-a7154579e94f')
     def test_delete_router_in_use(self):
@@ -49,6 +49,28 @@
             self.client.delete_router(self.router['id'])
 
 
+class RoutersNegativePolicyTest(RoutersNegativeTestBase):
+
+    credentials = ['admin', 'primary', 'alt']
+
+    @test.attr(type='negative')
+    @test.idempotent_id('159f576d-a423-46b5-b501-622694c02f6b')
+    def test_add_interface_wrong_tenant(self):
+        client2 = self.alt_manager.network_client
+        network = client2.create_network()['network']
+        self.addCleanup(client2.delete_network, network['id'])
+        subnet = self.create_subnet(network, client=client2)
+        # This port is deleted after a test by remove_router_interface.
+        port = client2.create_port(network_id=network['id'])['port']
+        self.addCleanup(client2.delete_port, port['id'])
+        with testtools.ExpectedException(lib_exc.NotFound):
+            client2.add_router_interface_with_port_id(
+                self.router['id'], port['id'])
+        with testtools.ExpectedException(lib_exc.NotFound):
+            client2.add_router_interface_with_subnet_id(
+                self.router['id'], subnet['id'])
+
+
 class DvrRoutersNegativeTest(RoutersNegativeTestBase):
 
     @classmethod