Merge "Tempest: Add l3-ha extension requirement for HA tests"
diff --git a/.zuul.yaml b/.zuul.yaml
index 636327e..adcb433 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -187,7 +187,6 @@
       - ^(test-|)requirements.txt$
       - ^releasenotes/.*$
       - ^setup.cfg$
-    voting: false
 
 - job:
     name: neutron-tempest-plugin-designate-scenario-queens
@@ -208,6 +207,7 @@
     gate:
       jobs:
         - neutron-tempest-plugin-api
+        - neutron-tempest-plugin-scenario-linuxbridge
         - build-openstack-sphinx-docs
 
 - project-template:
diff --git a/neutron_tempest_plugin/api/admin/test_quotas.py b/neutron_tempest_plugin/api/admin/test_quotas.py
index 1acfc18..ae773c8 100644
--- a/neutron_tempest_plugin/api/admin/test_quotas.py
+++ b/neutron_tempest_plugin/api/admin/test_quotas.py
@@ -121,17 +121,26 @@
         new_quotas = {'network': {'used': 1, 'limit': 2, 'reserved': 0},
                       'port': {'used': 1, 'limit': 2, 'reserved': 0}}
 
-        # update quota limit for tenant
-        new_quota = {'network': new_quotas['network']['limit'], 'port':
-                     new_quotas['port']['limit']}
-        quota_set = self._setup_quotas(tenant_id, **new_quota)
-
         # create test resources
         network = self._create_network(tenant_id)
         post_body = {"network_id": network['id'],
                      "tenant_id": tenant_id}
+
+        # NOTE(lucasagomes): Some backends such as OVN will create a port
+        # to be used by the metadata agent upon creating a network. In
+        # order to make this test more generic we need to calculate the
+        # number of expected used ports after the network is created and
+        # prior for the port being created
+        ports = self.admin_client.list_ports(tenant_id=tenant_id)
+        new_quotas['port']['used'] += len(ports['ports'])
+
         self._create_port(**post_body)
 
+        # update quota limit for tenant
+        new_quota = {'network': new_quotas['network']['limit'], 'port':
+                     new_quotas['port']['limit']}
+        quota_set = self._setup_quotas(tenant_id, **new_quota)
+
         # confirm from extended API quotas were changed
         # as requested for tenant
         quota_set = self.admin_client.show_details_quota(tenant_id)
diff --git a/neutron_tempest_plugin/api/base.py b/neutron_tempest_plugin/api/base.py
index fdd8ba9..6246eb7 100644
--- a/neutron_tempest_plugin/api/base.py
+++ b/neutron_tempest_plugin/api/base.py
@@ -284,8 +284,9 @@
         return network
 
     @classmethod
-    def create_subnet(cls, network, gateway=None, cidr=None, mask_bits=None,
-                      ip_version=None, client=None, **kwargs):
+    def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
+                      ip_version=None, client=None, reserve_cidr=True,
+                      **kwargs):
         """Wrapper utility that returns a test subnet.
 
         Convenient wrapper for client.create_subnet method. It reserves and
@@ -298,6 +299,7 @@
         It can be a str or a netaddr.IPAddress
         If gateway is not given, then it will use default address for
         given subnet CIDR, like "192.168.0.1" for "192.168.0.0/24" CIDR
+        if gateway is given as None then no gateway will be assigned
 
         :param cidr: CIDR of the subnet to create
         It can be either None, a str or a netaddr.IPNetwork instance
@@ -318,6 +320,10 @@
 
         :param client: client to be used to connect to network service
 
+        :param reserve_cidr: if True then it reserves assigned CIDR to avoid
+        using the same CIDR for further subnets in the scope of the same
+        test case class
+
         :param **kwargs: optional parameters to be forwarded to wrapped method
 
         [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets  # noqa
@@ -335,22 +341,23 @@
                         "Gateway IP version doesn't match IP version")
             else:
                 ip_version = gateway_ip.version
+        else:
+            ip_version = ip_version or cls._ip_version
 
         for subnet_cidr in cls.get_subnet_cidrs(
                 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
-            if cls.try_reserve_subnet_cidr(subnet_cidr):
-                gateway_ip = gateway or str(subnet_cidr.ip + 1)
-                try:
-                    body = client.create_subnet(
-                        network_id=network['id'],
-                        cidr=str(subnet_cidr),
-                        ip_version=subnet_cidr.version,
-                        gateway_ip=str(gateway_ip),
-                        **kwargs)
-                    break
-                except lib_exc.BadRequest as e:
-                    if 'overlaps with another subnet' not in str(e):
-                        raise
+            if gateway is not None:
+                kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
+            try:
+                body = client.create_subnet(
+                    network_id=network['id'],
+                    cidr=str(subnet_cidr),
+                    ip_version=subnet_cidr.version,
+                    **kwargs)
+                break
+            except lib_exc.BadRequest as e:
+                if 'overlaps with another subnet' not in str(e):
+                    raise
         else:
             message = 'Available CIDR for subnet creation could not be found'
             raise ValueError(message)
@@ -359,6 +366,8 @@
             cls.subnets.append(subnet)
         else:
             cls.admin_subnets.append(subnet)
+        if reserve_cidr:
+            cls.reserve_subnet_cidr(subnet_cidr)
         return subnet
 
     @classmethod
diff --git a/neutron_tempest_plugin/api/test_dhcp_ipv6.py b/neutron_tempest_plugin/api/test_dhcp_ipv6.py
index 69b4ea0..0fab75c 100644
--- a/neutron_tempest_plugin/api/test_dhcp_ipv6.py
+++ b/neutron_tempest_plugin/api/test_dhcp_ipv6.py
@@ -58,8 +58,8 @@
         ports = body['ports']
         for port in ports:
             if (port['device_owner'].startswith(
-                    constants.DEVICE_OWNER_ROUTER_INTF)
-                and port['device_id'] in [r['id'] for r in self.routers]):
+                    constants.DEVICE_OWNER_ROUTER_INTF) and
+                port['device_id'] in [r['id'] for r in self.routers]):
                 self.client.remove_router_interface_with_port_id(
                     port['device_id'], port['id']
                 )
diff --git a/neutron_tempest_plugin/api/test_network_ip_availability.py b/neutron_tempest_plugin/api/test_network_ip_availability.py
index ed56363..10aee2e 100644
--- a/neutron_tempest_plugin/api/test_network_ip_availability.py
+++ b/neutron_tempest_plugin/api/test_network_ip_availability.py
@@ -79,11 +79,11 @@
 def calc_total_ips(prefix, ip_version):
     # will calculate total ips after removing reserved.
     if ip_version == lib_constants.IP_VERSION_4:
-        total_ips = 2 ** (lib_constants.IPv4_BITS
-                          - prefix) - DEFAULT_IP4_RESERVED
+        total_ips = 2 ** (lib_constants.IPv4_BITS -
+                          prefix) - DEFAULT_IP4_RESERVED
     elif ip_version == lib_constants.IP_VERSION_6:
-        total_ips = 2 ** (lib_constants.IPv6_BITS
-                          - prefix) - DEFAULT_IP6_RESERVED
+        total_ips = 2 ** (lib_constants.IPv6_BITS -
+                          prefix) - DEFAULT_IP6_RESERVED
     return total_ips
 
 
diff --git a/neutron_tempest_plugin/api/test_routers.py b/neutron_tempest_plugin/api/test_routers.py
index e1b2eb1..4637dd6 100644
--- a/neutron_tempest_plugin/api/test_routers.py
+++ b/neutron_tempest_plugin/api/test_routers.py
@@ -160,9 +160,12 @@
         # 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, exception=AssertionError)
+
+        def _status_active():
+            return self.client.show_port(
+                intf['port_id'])['port']['status'] == 'ACTIVE'
+
+        utils.wait_until_true(_status_active, exception=AssertionError)
 
     @decorators.idempotent_id('c86ac3a8-50bd-4b00-a6b8-62af84a0765c')
     @tutils.requires_ext(extension='extraroute', service='network')
diff --git a/neutron_tempest_plugin/common/utils.py b/neutron_tempest_plugin/common/utils.py
index d6d0aee..c42d984 100644
--- a/neutron_tempest_plugin/common/utils.py
+++ b/neutron_tempest_plugin/common/utils.py
@@ -18,11 +18,12 @@
 
 """Utilities and helper functions."""
 
-import eventlet
 import functools
 import threading
 import time
 
+import eventlet
+
 
 class classproperty(object):
     def __init__(self, f):
diff --git a/neutron_tempest_plugin/scenario/base.py b/neutron_tempest_plugin/scenario/base.py
index 2bb6344..0a2fa14 100644
--- a/neutron_tempest_plugin/scenario/base.py
+++ b/neutron_tempest_plugin/scenario/base.py
@@ -302,9 +302,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):
diff --git a/neutron_tempest_plugin/scenario/test_floatingip.py b/neutron_tempest_plugin/scenario/test_floatingip.py
index 251f21c..bc40176 100644
--- a/neutron_tempest_plugin/scenario/test_floatingip.py
+++ b/neutron_tempest_plugin/scenario/test_floatingip.py
@@ -68,8 +68,8 @@
                 network_id=CONF.network.public_network_id)
 
             for subnet in subnets['subnets']:
-                if (subnet['gateway_ip']
-                    and subnet['ip_version'] == lib_constants.IP_VERSION_4):
+                if (subnet['gateway_ip'] and
+                    subnet['ip_version'] == lib_constants.IP_VERSION_4):
                     return subnet['gateway_ip']
 
     @classmethod
@@ -212,15 +212,6 @@
     def resource_setup(cls):
         super(FloatingIPQosTest, cls).resource_setup()
 
-    @classmethod
-    def skip_checks(cls):
-        super(FloatingIPQosTest, cls).skip_checks()
-        if utils.is_extension_enabled("dvr", "network"):
-            raise cls.skipException(
-                "Skip until bug "
-                "https://bugs.launchpad.net/neutron/+bug/1758316 "
-                "will be fixed.")
-
     @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
     def test_qos(self):
         """Test floating IP is binding to a QoS policy with
diff --git a/neutron_tempest_plugin/scenario/test_mtu.py b/neutron_tempest_plugin/scenario/test_mtu.py
index 8f1c9ed..b38d770 100644
--- a/neutron_tempest_plugin/scenario/test_mtu.py
+++ b/neutron_tempest_plugin/scenario/test_mtu.py
@@ -69,8 +69,8 @@
     def skip_checks(cls):
         super(NetworkMtuTest, cls).skip_checks()
         if ("vxlan" not in
-                config.CONF.neutron_plugin_options.available_type_drivers
-            or "gre" not in
+                config.CONF.neutron_plugin_options.available_type_drivers or
+            "gre" not in
                 config.CONF.neutron_plugin_options.available_type_drivers):
             raise cls.skipException("GRE or VXLAN type_driver is not enabled")
 
diff --git a/neutron_tempest_plugin/scenario/test_qos.py b/neutron_tempest_plugin/scenario/test_qos.py
index 58accb0..0611160 100644
--- a/neutron_tempest_plugin/scenario/test_qos.py
+++ b/neutron_tempest_plugin/scenario/test_qos.py
@@ -75,8 +75,8 @@
     BS = 512
     COUNT = BUFFER_SIZE / BS
     FILE_SIZE = BS * COUNT
-    LIMIT_BYTES_SEC = (constants.LIMIT_KILO_BITS_PER_SECOND * 1024
-                       * TOLERANCE_FACTOR / 8.0)
+    LIMIT_BYTES_SEC = (constants.LIMIT_KILO_BITS_PER_SECOND * 1024 *
+                       TOLERANCE_FACTOR / 8.0)
     FILE_PATH = "/tmp/img"
 
     NC_PORT = 1234
diff --git a/neutron_tempest_plugin/scenario/test_trunk.py b/neutron_tempest_plugin/scenario/test_trunk.py
index 6fdcd5b..2ff7e5d 100644
--- a/neutron_tempest_plugin/scenario/test_trunk.py
+++ b/neutron_tempest_plugin/scenario/test_trunk.py
@@ -34,7 +34,8 @@
     'sudo su -c '
     '"ip l a link $IFACE name $IFACE.%(tag)d type vlan id %(tag)d &&'
     'ip l s up dev $IFACE.%(tag)d && '
-    'dhclient $IFACE.%(tag)d"')
+    '{ ps -ef | grep -q "dhclient .*$IFACE.%(tag)d" || '
+    'dhclient $IFACE.%(tag)d"; }')
 
 
 class TrunkTest(base.BaseTempestTestCase):
@@ -230,7 +231,7 @@
         vlan_tag = 10
 
         vlan_network = self.create_network()
-        self.create_subnet(vlan_network)
+        self.create_subnet(vlan_network, gateway=None)
 
         servers = [
             self._create_server_with_port_and_subport(vlan_network, vlan_tag)
diff --git a/test-requirements.txt b/test-requirements.txt
index b8835e3..84f3c18 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -5,6 +5,7 @@
 hacking<0.13,>=0.12.0 # Apache-2.0
 
 coverage!=4.4,>=4.0 # Apache-2.0
+flake8-import-order==0.12 # LGPLv3
 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
diff --git a/tox.ini b/tox.ini
index 06eda94..d966308 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 minversion = 2.0
-envlist = py34,py27,pypy,pep8
+envlist = pep8
 skipsdist = True
 
 [testenv]
@@ -15,7 +15,7 @@
 [testenv:pep8]
 commands =
   sh ./tools/misc-sanity-checks.sh
-  flake8 {posargs}
+  flake8
 whitelist_externals =
   sh