Merge "Add retry on Conflict for manual speaker bind" into mcp/caracal
diff --git a/neutron_tempest_plugin/api/admin/test_quotas.py b/neutron_tempest_plugin/api/admin/test_quotas.py
index 0cf474e..8d96695 100644
--- a/neutron_tempest_plugin/api/admin/test_quotas.py
+++ b/neutron_tempest_plugin/api/admin/test_quotas.py
@@ -12,6 +12,7 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+import time
 
 from tempest.common import utils
 from tempest.lib import decorators
@@ -50,6 +51,8 @@
     def _create_network(self, project_id):
         network = self.create_network(client=self.admin_client,
                                       tenant_id=project_id)
+        # Give some time to setup internal ports
+        time.sleep(CONF.neutron_plugin_options.internal_port_setup_delay)
         self.addCleanup(self.admin_client.delete_network,
                         network['id'])
         return network
@@ -87,7 +90,7 @@
         new_quotas = {'network': 0, 'security_group': 0}
 
         # Change quotas for tenant
-        quota_set = self._setup_quotas(tenant_id, **new_quotas)
+        quota_set = self._setup_quotas(tenant_id, force=True, **new_quotas)
         for key, value in new_quotas.items():
             self.assertEqual(value, quota_set[key])
 
@@ -112,6 +115,23 @@
         for q in non_default_quotas['quotas']:
             self.assertNotEqual(tenant_id, q['tenant_id'])
 
+    @decorators.idempotent_id('43d01327-d8be-4773-a8f0-1d2e9664fda2')
+    @decorators.attr(type='gate')
+    @utils.requires_ext(extension='quota-check-limit-default',
+                        service='network')
+    def test_quotas_force_false(self):
+        project_id = self.create_project()['id']
+        self._create_network(project_id)
+
+        new_quotas = {'network': 0}
+        # force=false (by default)
+        self.assertRaises(lib_exc.BadRequest, self.admin_client.update_quotas,
+                          project_id, **new_quotas)
+
+        new_quotas['network'] = 100
+        quota_set = self._setup_quotas(project_id, **new_quotas)
+        self.assertEqual(new_quotas['network'], quota_set['network'])
+
     @decorators.idempotent_id('e974b5ba-090a-452c-a578-f9710151d9fc')
     @decorators.attr(type='gate')
     @utils.requires_ext(extension="quota_details", service="network")
diff --git a/neutron_tempest_plugin/api/admin/test_quotas_negative.py b/neutron_tempest_plugin/api/admin/test_quotas_negative.py
index 9c37d92..ae7c60f 100644
--- a/neutron_tempest_plugin/api/admin/test_quotas_negative.py
+++ b/neutron_tempest_plugin/api/admin/test_quotas_negative.py
@@ -13,6 +13,7 @@
 from neutron_lib import constants
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
@@ -24,6 +25,13 @@
 
 class QuotasAdminNegativeTestJSON(test_quotas.QuotasTestBase):
 
+    def wait_network_ports(self, network_id, number):
+        def _wait_ports(network_id, number):
+            ports = self.admin_client.list_ports(
+                network_id=network_id)
+            return len(ports) == number
+        test_utils.call_until_true(_wait_ports, 90, 5, network_id, number)
+
     @decorators.attr(type='negative')
     @decorators.idempotent_id('952f9b24-9156-4bdc-90f3-682a3d4302f0')
     def test_create_network_when_quotas_is_full(self):
@@ -74,6 +82,9 @@
                        'cidr': '10.0.0.0/24',
                        'ip_version': '4'}
         subnet = self.admin_client.create_subnet(**subnet_args)['subnet']
+        self.wait_network_ports(subnet["network_id"],
+                                CONF.network.service_ports_number)
+
         self.addCleanup(self.admin_client.delete_subnet, subnet['id'])
 
         ports = self.admin_client.list_ports(tenant_id=tenant_id)
diff --git a/neutron_tempest_plugin/api/base.py b/neutron_tempest_plugin/api/base.py
index 5a1de9c..0897c35 100644
--- a/neutron_tempest_plugin/api/base.py
+++ b/neutron_tempest_plugin/api/base.py
@@ -1225,6 +1225,16 @@
         client = client or ndp_proxy.get('client') or cls.client
         client.delete_ndp_proxy(ndp_proxy['id'])
 
+    @classmethod
+    def get_loaded_network_extensions(cls):
+        """Return the network service loaded extensions
+
+        :return: list of strings with the alias of the network service loaded
+                 extensions.
+        """
+        body = cls.client.list_extensions()
+        return [net_ext['alias'] for net_ext in body['extensions']]
+
 
 class BaseAdminNetworkTest(BaseNetworkTest):
 
@@ -1460,6 +1470,10 @@
     def _extract_resources(cls, body):
         return body[cls.plural_name]
 
+    @classmethod
+    def _test_resources(cls, resources):
+        return [res for res in resources if res["name"] in cls.resource_names]
+
     def _test_list_sorts(self, direction):
         sort_args = {
             'sort_dir': direction,
@@ -1511,11 +1525,12 @@
             'sort_key': self.field
         }
         body = self.list_method(**sort_args)
-        expected_resources = self._extract_resources(body)
+        total_resources = self._extract_resources(body)
+        expected_resources = self._test_resources(total_resources)
         self.assertNotEmpty(expected_resources)
 
         resources = lister(
-            len(expected_resources), sort_args
+            len(total_resources), sort_args
         )
 
         # finally, compare that the list retrieved in one go is identical to
@@ -1533,9 +1548,11 @@
                 pagination_args['marker'] = resources[-1]['id']
             body = self.list_method(**pagination_args)
             resources_ = self._extract_resources(body)
-            self.assertEqual(1, len(resources_))
+            # Empty resource list can be returned when any concurrent
+            # tests delete them
+            self.assertGreaterEqual(1, len(resources_))
             resources.extend(resources_)
-        return resources
+        return self._test_resources(resources)
 
     @_require_pagination
     @_require_sorting
@@ -1558,8 +1575,10 @@
                 self.plural_name, uri
             )
             resources_ = self._extract_resources(body)
-            self.assertEqual(1, len(resources_))
-            resources.extend(resources_)
+            # Empty resource list can be returned when any concurrent
+            # tests delete them
+            self.assertGreaterEqual(1, len(resources_))
+            resources.extend(self._test_resources(resources_))
 
         # The last element is empty and does not contain 'next' link
         uri = self.get_bare_url(prev_links['next'])
@@ -1576,8 +1595,10 @@
                 self.plural_name, uri
             )
             resources_ = self._extract_resources(body)
-            self.assertEqual(1, len(resources_))
-            resources2.extend(resources_)
+            # Empty resource list can be returned when any concurrent
+            # tests delete them
+            self.assertGreaterEqual(1, len(resources_))
+            resources2.extend(self._test_resources(resources_))
 
         self.assertSameOrder(resources, reversed(resources2))
 
@@ -1597,14 +1618,15 @@
             'sort_key': self.field,
         }
         body = self.list_method(**pagination_args)
-        expected_resources = self._extract_resources(body)
+        total_resources = self._extract_resources(body)
+        expected_resources = self._test_resources(total_resources)
 
         page_size = 2
         pagination_args['limit'] = page_size
 
         prev_links = {}
         resources = []
-        num_resources = len(expected_resources)
+        num_resources = len(total_resources)
         niterations = int(math.ceil(float(num_resources) / page_size))
         for i in range(niterations):
             if prev_links:
@@ -1616,7 +1638,7 @@
             prev_links, body = self.list_client.get_uri_with_links(
                 self.plural_name, uri
             )
-            resources_ = self._extract_resources(body)
+            resources_ = self._test_resources(self._extract_resources(body))
             self.assertGreaterEqual(page_size, len(resources_))
             resources.extend(reversed(resources_))
 
diff --git a/neutron_tempest_plugin/api/test_security_groups.py b/neutron_tempest_plugin/api/test_security_groups.py
index 4f39cc6..c3b5f11 100644
--- a/neutron_tempest_plugin/api/test_security_groups.py
+++ b/neutron_tempest_plugin/api/test_security_groups.py
@@ -284,9 +284,10 @@
     def _set_quota(self, val, resource):
         res_quota = self._get_quota(resource)
         project_id = self.client.project_id
-        self.admin_client.update_quotas(project_id, **{resource: val})
+        self.admin_client.update_quotas(project_id, **{resource: val,
+                                                       'force': True})
         self.addCleanup(self.admin_client.update_quotas,
-                        project_id, **{resource: res_quota})
+                        project_id, **{resource: res_quota, 'force': True})
 
     def _get_quota(self, resource):
         project_id = self.client.project_id
@@ -386,7 +387,8 @@
     def _set_sg_rules_quota(self, val):
         project_id = self.client.project_id
         self.admin_client.update_quotas(project_id,
-                                        **{'security_group_rule': val})
+                                        **{'security_group_rule': val,
+                                           'force': True})
         LOG.info('Trying to update security group rule quota {} '.format(val))
 
     def _get_sg_rules_quota(self):
@@ -438,7 +440,8 @@
         sg_rules_quota = self._get_sg_rules_quota()
         project_id = self.client.project_id
         self.addCleanup(self.admin_client.update_quotas,
-                        project_id, **{'security_group_rule': sg_rules_quota})
+                        project_id, **{'security_group_rule': sg_rules_quota,
+                                       'force': True})
         values = [-1, 0, 10, 2147483647]
         for value in values:
             self._set_sg_rules_quota(value)
diff --git a/neutron_tempest_plugin/api/test_subnets.py b/neutron_tempest_plugin/api/test_subnets.py
index 3b075d5..663713c 100644
--- a/neutron_tempest_plugin/api/test_subnets.py
+++ b/neutron_tempest_plugin/api/test_subnets.py
@@ -24,6 +24,8 @@
 
     @classmethod
     def resource_setup(cls):
+        if 'subnet-external-network' in cls.get_loaded_network_extensions():
+            cls.list_kwargs['router:external'] = False
         super(SubnetsSearchCriteriaTest, cls).resource_setup()
         net = cls.create_network(network_name='subnet-search-test-net')
         for name in cls.resource_names:
@@ -67,6 +69,8 @@
 
     @decorators.idempotent_id('c0f9280b-9d81-4728-a967-6be22659d4c8')
     def test_list_validation_filters(self):
+        if 'subnet-external-network' in self.get_loaded_network_extensions():
+            self.list_kwargs['router:external'] = False
         self._test_list_validation_filters(self.list_kwargs)
         self._test_list_validation_filters({
             'unknown_filter': 'value'}, filter_is_valid=False)
diff --git a/neutron_tempest_plugin/bgpvpn/scenario/manager.py b/neutron_tempest_plugin/bgpvpn/scenario/manager.py
index f5e9bf2..b65407d 100644
--- a/neutron_tempest_plugin/bgpvpn/scenario/manager.py
+++ b/neutron_tempest_plugin/bgpvpn/scenario/manager.py
@@ -241,10 +241,14 @@
         if not subnets_client:
             subnets_client = self.subnets_client
         tenant_cidr = kwargs.get('cidr')
+        # reserving pool for Neutron service ports
+        net = netaddr.IPNetwork(tenant_cidr)
+        allocation_pools = [{'start': str(net[2]), 'end': str(net[9])}]
         subnet = dict(
             name=data_utils.rand_name(namestart),
             network_id=network['id'],
             tenant_id=network['tenant_id'],
+            allocation_pools=allocation_pools,
             **kwargs)
         result = subnets_client.create_subnet(**subnet)
         self.assertIsNotNone(result, 'Unable to allocate tenant network')
diff --git a/neutron_tempest_plugin/config.py b/neutron_tempest_plugin/config.py
index 3f8ccd5..016299b 100644
--- a/neutron_tempest_plugin/config.py
+++ b/neutron_tempest_plugin/config.py
@@ -151,6 +151,10 @@
     cfg.BoolOpt('bgp_schedule_speakers_to_agents',
                 default=False,
                 help='Schedule BGP speakers to agents explicitly.'),
+    cfg.IntOpt('internal_port_setup_delay',
+           default=5,
+           help='Number of seconds to wait after network create to '
+                'setup internal ports.'),
 ]
 
 # TODO(amuller): Redo configuration options registration as part of the planned
diff --git a/neutron_tempest_plugin/scenario/test_dns_integration.py b/neutron_tempest_plugin/scenario/test_dns_integration.py
index 3047c4d..fa859b8 100644
--- a/neutron_tempest_plugin/scenario/test_dns_integration.py
+++ b/neutron_tempest_plugin/scenario/test_dns_integration.py
@@ -64,7 +64,7 @@
     def setup_clients(cls):
         super(BaseDNSIntegrationTests, cls).setup_clients()
         cls.dns_client = cls.os_tempest.dns_v2.ZonesClient()
-        cls.query_client.build_timeout = 60
+        cls.query_client.build_timeout = CONF.dns.build_timeout
         if CONF.enforce_scope.designate:
             cls.admin_tld_client = cls.os_system_admin.dns_v2.TldClient()
             cls.rec_client = cls.os_system_admin.dns_v2.RecordsetClient()
diff --git a/neutron_tempest_plugin/scenario/test_floatingip.py b/neutron_tempest_plugin/scenario/test_floatingip.py
index 4c68dae..340a6be 100644
--- a/neutron_tempest_plugin/scenario/test_floatingip.py
+++ b/neutron_tempest_plugin/scenario/test_floatingip.py
@@ -239,9 +239,13 @@
                 self.os_primary.interfaces_client, server['server']['id'],
                 port['id'], lib_constants.PORT_STATUS_ACTIVE)
             fip = self.client.show_floatingip(fip['id'])['floatingip']
+            server_data = self.os_admin.servers_client.show_server(
+                server['server']['id'])['server']
+            zone = 'compute:' + server_data['OS-EXT-AZ:availability_zone']
             self._check_port_details(
                 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
-                device_id=server['server']['id'], device_owner='compute:nova')
+                device_id=server['server']['id'],
+                device_owner=zone)
             LOG.debug('Port check for server %s and FIP %s finished, '
                       'lets detach port %s from server!',
                       server['server']['id'], fip['id'], port['id'])
diff --git a/neutron_tempest_plugin/scenario/test_security_groups.py b/neutron_tempest_plugin/scenario/test_security_groups.py
index ae6c57d..dc92342 100644
--- a/neutron_tempest_plugin/scenario/test_security_groups.py
+++ b/neutron_tempest_plugin/scenario/test_security_groups.py
@@ -579,6 +579,9 @@
             # configure sec groups to support SSH connectivity
             self.create_loginable_secgroup_rule(
                 secgroup_id=secgroups[-1]['id'])
+            # add the initial metadata access rule if VMs are not booted yet
+            self.create_ingress_metadata_secgroup_rule(
+                secgroup_id=secgroups[-1]['id'])
 
         # Configure security groups, first two servers as remotes
         for i, server in enumerate(servers):
diff --git a/requirements.txt b/requirements.txt
index 481bb7f..029edf6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,3 +18,4 @@
 eventlet!=0.18.3,!=0.20.1,>=0.18.2 # MIT
 debtcollector>=1.2.0 # Apache-2.0
 docker  # Apache-2.0
+Jinja2  # BSD License (3 clause)