Merge "Limit execution of the neutron CI jobs on some irrelevant file"
diff --git a/neutron_tempest_plugin/api/test_routers.py b/neutron_tempest_plugin/api/test_routers.py
index d866dbc..5e916f5 100644
--- a/neutron_tempest_plugin/api/test_routers.py
+++ b/neutron_tempest_plugin/api/test_routers.py
@@ -15,9 +15,12 @@
 
 import netaddr
 
+from neutron_lib import constants as const
+
 from tempest.common import utils as tutils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 from neutron_tempest_plugin.api import base
 from neutron_tempest_plugin.api import base_routers
@@ -453,3 +456,57 @@
     @decorators.idempotent_id('fb102124-20f8-4cb3-8c81-f16f5e41d192')
     def test_list_no_pagination_limit_0(self):
         self._test_list_no_pagination_limit_0()
+
+
+class RoutersDeleteTest(base_routers.BaseRouterTest):
+    """The only test in this class is a test that removes router!
+
+    * We cannot delete common and mandatory resources (router in this case)
+    * using the existing classes, as it will cause failure in other tests
+    * running in parallel.
+    """
+    @classmethod
+    def resource_setup(cls):
+        super(RoutersDeleteTest, cls).resource_setup()
+        cls.secgroup = cls.create_security_group(
+            name=data_utils.rand_name("test_port_secgroup"))
+        router_kwargs = {
+            'router_name': data_utils.rand_name('router_to_delete'),
+            'external_network_id': CONF.network.public_network_id}
+        cls.router = cls.create_router(**router_kwargs)
+
+    @decorators.idempotent_id('dbbc5c74-63c8-11eb-8881-74e5f9e2a801')
+    def test_delete_router(self):
+        # Create a port on tenant network and associate to the router.
+        # Try to delete router. Expected result: "Conflict Error" is raised.
+        network = self.create_network()
+        subnet = self.create_subnet(network)
+        self.create_router_interface(self.router['id'], subnet['id'])
+        port = self.create_port(
+            network, name=data_utils.rand_name("port"),
+            security_groups=[self.secgroup['id']])
+        self.create_floatingip(port=port)
+        self.assertRaises(
+            lib_exc.Conflict, self.client.delete_router, self.router['id'])
+        # Delete the associated port
+        # Try to delete router. Expected result: "Conflict Error" is raised.
+        # Note: there are still interfaces in use.
+        self.client.delete_port(port['id'])
+        self.assertRaises(
+            lib_exc.Conflict, self.client.delete_router, self.router['id'])
+        # Delete the rest of the router's ports
+        # Try to delete router. Expected result: "PASS"
+        interfaces = [
+            port for port in self.client.list_router_interfaces(
+                self.router['id'])['ports']
+            if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
+        for i in interfaces:
+            try:
+                self.assertRaises(
+                    lib_exc.Conflict, self.client.delete_router,
+                    self.router['id'])
+                self.client.remove_router_interface_with_subnet_id(
+                    self.router['id'], i['fixed_ips'][0]['subnet_id'])
+            except lib_exc.NotFound:
+                pass
+        self.client.delete_router(self.router['id'])
diff --git a/neutron_tempest_plugin/api/test_routers_negative.py b/neutron_tempest_plugin/api/test_routers_negative.py
index 86d58e2..a4b3619 100644
--- a/neutron_tempest_plugin/api/test_routers_negative.py
+++ b/neutron_tempest_plugin/api/test_routers_negative.py
@@ -37,19 +37,6 @@
         cls.subnet = cls.create_subnet(cls.network)
 
 
-class RoutersNegativeTest(RoutersNegativeTestBase):
-
-    @decorators.attr(type='negative')
-    @decorators.idempotent_id('e3e751af-15a2-49cc-b214-a7154579e94f')
-    def test_delete_router_in_use(self):
-        # This port is deleted after a test by remove_router_interface.
-        port = self.create_port(self.network)
-        self.client.add_router_interface_with_port_id(
-            self.router['id'], port['id'])
-        with testtools.ExpectedException(lib_exc.Conflict):
-            self.client.delete_router(self.router['id'])
-
-
 class RoutersNegativePolicyTest(RoutersNegativeTestBase):
 
     credentials = ['admin', 'primary', 'alt']
diff --git a/neutron_tempest_plugin/scenario/test_metadata.py b/neutron_tempest_plugin/scenario/test_metadata.py
index 91ecc97..05f0f04 100644
--- a/neutron_tempest_plugin/scenario/test_metadata.py
+++ b/neutron_tempest_plugin/scenario/test_metadata.py
@@ -23,6 +23,7 @@
 
 from neutron_tempest_plugin.common import ssh
 from neutron_tempest_plugin import config
+from neutron_tempest_plugin import exceptions
 from neutron_tempest_plugin.scenario import base
 
 LOG = logging.getLogger(__name__)
@@ -133,17 +134,22 @@
             self.network, use_advanced_image=use_advanced_image)
         self.wait_for_server_active(server=vm.server)
         self.wait_for_guest_os_ready(vm.server)
-        self._assert_has_ssh_connectivity(vm.ssh_client)
+        self.check_connectivity(host=vm.floating_ip['floating_ip_address'],
+                                ssh_client=vm.ssh_client)
         interface = self._get_primary_interface(vm.ssh_client)
 
-        out = vm.ssh_client.exec_command(
-            'curl http://[%(address)s%%25%(interface)s]/' % {
-                'address': nlib_const.METADATA_V6_IP,
-                'interface': interface})
-        self.assertIn('latest', out)
+        try:
+            out = vm.ssh_client.exec_command(
+                'curl http://[%(address)s%%25%(interface)s]/' % {
+                    'address': nlib_const.METADATA_V6_IP,
+                    'interface': interface})
+            self.assertIn('latest', out)
 
-        out = vm.ssh_client.exec_command(
-            'curl http://[%(address)s%%25%(interface)s]/openstack/' % {
-                'address': nlib_const.METADATA_V6_IP,
-                'interface': interface})
-        self.assertIn('latest', out)
+            out = vm.ssh_client.exec_command(
+                'curl http://[%(address)s%%25%(interface)s]/openstack/' % {
+                    'address': nlib_const.METADATA_V6_IP,
+                    'interface': interface})
+            self.assertIn('latest', out)
+        except exceptions.SSHExecCommandFailed:
+            self._log_console_output()
+            self._log_local_network_status()