Merge "Switch neutron-tempest-plugin-api job to zuul v3 format"
diff --git a/.gitignore b/.gitignore
index 963e589..a678dd2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,8 +27,8 @@
 !.coveragerc
 .tox
 nosetests.xml
-.testrepository
 .venv
+.stestr/
 
 # Translations
 *.mo
diff --git a/.stestr.conf b/.stestr.conf
new file mode 100644
index 0000000..7a46f32
--- /dev/null
+++ b/.stestr.conf
@@ -0,0 +1,3 @@
+[DEFAULT]
+test_path=${OS_TEST_PATH:-./neutron_tempest_plugin}
+top_dir=./
diff --git a/.testr.conf b/.testr.conf
deleted file mode 100644
index 6d83b3c..0000000
--- a/.testr.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-[DEFAULT]
-test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
-             OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
-             OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
-             ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
-test_id_option=--load-list $IDFILE
-test_list_option=--list
diff --git a/.zuul.yaml b/.zuul.yaml
index 27780d0..247b100 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -212,7 +212,6 @@
       - ^(test-|)requirements.txt$
       - ^releasenotes/.*$
       - ^setup.cfg$
-    voting: false
 
 - job:
     name: neutron-tempest-plugin-designate-scenario-queens
diff --git a/neutron_tempest_plugin/api/admin/test_routers_dvr.py b/neutron_tempest_plugin/api/admin/test_routers_dvr.py
index 2313d1b..644bc38 100644
--- a/neutron_tempest_plugin/api/admin/test_routers_dvr.py
+++ b/neutron_tempest_plugin/api/admin/test_routers_dvr.py
@@ -19,7 +19,7 @@
 from neutron_tempest_plugin.api import base_routers as base
 
 
-class RoutersTestDVR(base.BaseRouterTest):
+class RoutersTestDVRBase(base.BaseRouterTest):
 
     required_extensions = ['router', 'dvr']
 
@@ -31,7 +31,7 @@
         # admin credentials to create router with distributed=True attribute
         # and checking for BadRequest exception and that the resulting router
         # has a distributed attribute.
-        super(RoutersTestDVR, cls).resource_setup()
+        super(RoutersTestDVRBase, cls).resource_setup()
         name = data_utils.rand_name('pretest-check')
         router = cls.admin_client.create_router(name)
         if 'distributed' not in router['router']:
@@ -39,6 +39,9 @@
             raise cls.skipException(msg)
         cls.admin_client.delete_router(router['router']['id'])
 
+
+class RoutersTestDVR(RoutersTestDVRBase):
+
     @decorators.idempotent_id('08a2a0a8-f1e4-4b34-8e30-e522e836c44e')
     def test_distributed_router_creation(self):
         """
@@ -74,6 +77,11 @@
                         router['router']['id'])
         self.assertFalse(router['router']['distributed'])
 
+
+class RouterTestCentralizedToDVR(RoutersTestDVRBase):
+
+    required_extensions = ['l3-ha']
+
     @decorators.idempotent_id('acd43596-c1fb-439d-ada8-31ad48ae3c2e')
     def test_centralized_router_update_to_dvr(self):
         """
diff --git a/neutron_tempest_plugin/api/test_routers.py b/neutron_tempest_plugin/api/test_routers.py
index d5d2e04..4637dd6 100644
--- a/neutron_tempest_plugin/api/test_routers.py
+++ b/neutron_tempest_plugin/api/test_routers.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 import netaddr
+
 from tempest.common import utils as tutils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
@@ -244,6 +245,11 @@
                         self.admin_client)
         self.assertTrue(create_body['router']['distributed'])
 
+
+class DvrRoutersTestToCentralized(base_routers.BaseRouterTest):
+
+    required_extensions = ['dvr', 'l3-ha']
+
     @decorators.idempotent_id('644d7a4a-01a1-4b68-bb8d-0c0042cb1729')
     def test_convert_centralized_router(self):
         router_args = {'tenant_id': self.client.tenant_id,
diff --git a/neutron_tempest_plugin/config.py b/neutron_tempest_plugin/config.py
index 65282cf..fc07e81 100644
--- a/neutron_tempest_plugin/config.py
+++ b/neutron_tempest_plugin/config.py
@@ -11,7 +11,6 @@
 #    under the License.
 
 from oslo_config import cfg
-
 from tempest import config
 
 
@@ -47,6 +46,17 @@
                help='The agent mode for L3 agents in the deployment. '
                     'Configure this only when the single value is used by '
                     'all agents in the deployment.'),
+    cfg.StrOpt('test_mtu_networks',
+               default='[{"provider:network_type":"vxlan",'
+                       '"mtu":1200, "cidr":"10.100.0.0/16"}'
+                       ','
+                       '{"provider:network_type":"vxlan",'
+                       '"mtu":1300, "cidr":"10.200.0.0/16"}]',
+               help='Configuration for test networks. The format is JSON. '
+                    '"provider:network_type":<TYPE> - string '
+                    '"mtu":<MTU> - integer '
+                    '"cidr"<SUBNET/MASK> - string '
+                    '"provider:segmentation_id":<VLAN_ID> - integer')
 ]
 
 # TODO(amuller): Redo configuration options registration as part of the planned
diff --git a/neutron_tempest_plugin/scenario/base.py b/neutron_tempest_plugin/scenario/base.py
index 2bb6344..b76a81a 100644
--- a/neutron_tempest_plugin/scenario/base.py
+++ b/neutron_tempest_plugin/scenario/base.py
@@ -218,9 +218,7 @@
             key_name=self.keypair['name'],
             networks=[{'uuid': self.network['id']}],
             security_groups=[{'name': secgroup['security_group']['name']}])
-        waiters.wait_for_server_status(self.os_primary.servers_client,
-                                       self.server['server']['id'],
-                                       constants.SERVER_STATUS_ACTIVE)
+        self.wait_for_server_active(self.server['server'])
         self.port = self.client.list_ports(network_id=self.network['id'],
                                            device_id=self.server[
                                                'server']['id'])['ports'][0]
@@ -302,9 +300,18 @@
                                           1)
 
     def check_remote_connectivity(self, source, dest, should_succeed=True,
-                                  nic=None, mtu=None, fragmentation=True):
-        self.assertTrue(self._check_remote_connectivity(
-            source, dest, should_succeed, nic, mtu, fragmentation))
+                                  nic=None, mtu=None, fragmentation=True,
+                                  servers=None):
+        try:
+            self.assertTrue(self._check_remote_connectivity(
+                source, dest, should_succeed, nic, mtu, fragmentation))
+        except lib_exc.SSHTimeout as ssh_e:
+            LOG.debug(ssh_e)
+            self._log_console_output(servers)
+            raise
+        except AssertionError:
+            self._log_console_output(servers)
+            raise
 
     def ping_ip_address(self, ip_address, should_succeed=True,
                         ping_timeout=None, mtu=None):
@@ -343,3 +350,25 @@
                       'result': 'expected' if result else 'unexpected'
                   })
         return result
+
+    def wait_for_server_status(self, server, status, client=None, **kwargs):
+        """Waits for a server to reach a given status.
+
+        :param server:  mapping having schema {'id': <server_id>}
+        :param status: string status to wait for (es: 'ACTIVE')
+        :param clien:  servers client (self.os_primary.servers_client as
+                       default value)
+        """
+
+        client = client or self.os_primary.servers_client
+        waiters.wait_for_server_status(client, server['id'], status, **kwargs)
+
+    def wait_for_server_active(self, server, client=None):
+        """Waits for a server to reach active status.
+
+        :param server:  mapping having schema {'id': <server_id>}
+        :param clien:  servers client (self.os_primary.servers_client as
+                       default value)
+        """
+        self.wait_for_server_status(
+            server, constants.SERVER_STATUS_ACTIVE, client)
diff --git a/neutron_tempest_plugin/scenario/test_mtu.py b/neutron_tempest_plugin/scenario/test_mtu.py
index b38d770..0e3afe9 100644
--- a/neutron_tempest_plugin/scenario/test_mtu.py
+++ b/neutron_tempest_plugin/scenario/test_mtu.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from neutron_lib.api.definitions import provider_net
+from oslo_serialization import jsonutils
 from tempest.common import utils
 from tempest.common import waiters
 from tempest.lib.common.utils import data_utils
@@ -59,6 +60,9 @@
         fip = self.create_and_associate_floatingip(port['id'])
         return server, fip
 
+    def _get_network_params(self):
+        return jsonutils.loads(CONF.neutron_plugin_options.test_mtu_networks)
+
 
 class NetworkMtuTest(NetworkMtuBaseTest):
     credentials = ['primary', 'admin']
@@ -97,13 +101,13 @@
         self.assertNotEqual(self.networks[0]['mtu'], self.networks[1]['mtu'])
         self.networks.sort(key=lambda net: net['mtu'])
         server1, fip1 = self.create_pingable_vm(self.networks[0],
-            self.keypair, self.secgroup)
+                                                self.keypair, self.secgroup)
         server_ssh_client1 = ssh.Client(
             self.floating_ips[0]['floating_ip_address'],
             CONF.validation.image_ssh_user,
             pkey=self.keypair['private_key'])
         server2, fip2 = self.create_pingable_vm(self.networks[1],
-            self.keypair, self.secgroup)
+                                                self.keypair, self.secgroup)
         server_ssh_client2 = ssh.Client(
             self.floating_ips[0]['floating_ip_address'],
             CONF.validation.image_ssh_user,
@@ -150,7 +154,7 @@
     def skip_checks(cls):
         super(NetworkWritableMtuTest, cls).skip_checks()
         if ("vxlan" not in
-            config.CONF.neutron_plugin_options.available_type_drivers):
+                config.CONF.neutron_plugin_options.available_type_drivers):
             raise cls.skipException("VXLAN type_driver is not enabled")
 
     @classmethod
@@ -160,24 +164,24 @@
 
     def _create_setup(self):
         self.admin_client = self.os_admin.network_client
-        net_kwargs = {'tenant_id': self.client.tenant_id,
-                      'provider:network_type': 'vxlan'}
-        for _ in range(2):
-            net_kwargs['name'] = data_utils.rand_name('net')
-            network = self.admin_client.create_network(**net_kwargs)[
+        for test_net in self._get_network_params():
+            test_net['tenant_id'] = self.client.tenant_id
+            test_net['name'] = data_utils.rand_name('net')
+            cidr = None if 'cidr' not in test_net else test_net.pop('cidr')
+            network = self.admin_client.create_network(**test_net)[
                 'network']
             self.networks.append(network)
             self.addCleanup(self.admin_client.delete_network, network['id'])
-            subnet = self.create_subnet(network)
+            subnet = self.create_subnet(network, cidr=cidr)
             self.create_router_interface(self.router['id'], subnet['id'])
             self.addCleanup(self.client.remove_router_interface_with_subnet_id,
                             self.router['id'], subnet['id'])
 
-        # Update network mtu.
+        # update network mtu
         net_mtu = self.admin_client.show_network(
             self.networks[0]['id'])['network']['mtu']
         self.admin_client.update_network(self.networks[0]['id'],
-            mtu=(net_mtu - 1))
+                                         mtu=(net_mtu - 1))
         self.networks[0]['mtu'] = (
             self.admin_client.show_network(
                 self.networks[0]['id'])['network']['mtu'])
@@ -186,13 +190,13 @@
         self.assertNotEqual(self.networks[0]['mtu'], self.networks[1]['mtu'])
         self.networks.sort(key=lambda net: net['mtu'])
         server1, fip1 = self.create_pingable_vm(self.networks[0],
-            self.keypair, self.secgroup)
+                                                self.keypair, self.secgroup)
         server_ssh_client1 = ssh.Client(
             self.floating_ips[0]['floating_ip_address'],
             CONF.validation.image_ssh_user,
             pkey=self.keypair['private_key'])
         server2, fip2 = self.create_pingable_vm(self.networks[1],
-            self.keypair, self.secgroup)
+                                                self.keypair, self.secgroup)
         server_ssh_client2 = ssh.Client(
             self.floating_ips[0]['floating_ip_address'],
             CONF.validation.image_ssh_user,
diff --git a/test-requirements.txt b/test-requirements.txt
index 84f3c18..c0546cf 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -9,7 +9,7 @@
 python-subunit>=1.0.0 # Apache-2.0/BSD
 sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
 oslotest>=3.2.0 # Apache-2.0
-testrepository>=0.0.18 # Apache-2.0/BSD
+stestr>=1.0.0 # Apache-2.0
 testtools>=2.2.0 # MIT
 openstackdocstheme>=1.18.1 # Apache-2.0
 # releasenotes
diff --git a/tox.ini b/tox.ini
index d966308..bba0a64 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,8 +9,11 @@
 setenv =
    VIRTUAL_ENV={envdir}
    PYTHONWARNINGS=default::DeprecationWarning
+   OS_LOG_CAPTURE={env:OS_LOG_CAPTURE:true}
+   OS_STDOUT_CAPTURE={env:OS_STDOUT_CAPTURE:true}
+   OS_STDERR_CAPTURE={env:OS_STDERR_CAPTURE:true}
 deps = -r{toxinidir}/test-requirements.txt
-commands = python setup.py test --slowest --testr-args='{posargs}'
+commands = stestr run --slowest {posargs}
 
 [testenv:pep8]
 commands =
@@ -23,7 +26,14 @@
 commands = {posargs}
 
 [testenv:cover]
-commands = python setup.py test --coverage --testr-args='{posargs}'
+setenv =
+    {[testenv]setenv}
+    PYTHON=coverage run --source neutron_tempest_plugin --parallel-mode
+commands =
+    stestr run --no-subunit-trace {posargs}
+    coverage combine
+    coverage html -d cover
+    coverage xml -o cover/coverage.xml
 
 [testenv:docs]
 commands = python setup.py build_sphinx