Merge "Switch networking-sfc job to be run with eventlet"
diff --git a/neutron_tempest_plugin/api/base.py b/neutron_tempest_plugin/api/base.py
index 99fa946..e260313 100644
--- a/neutron_tempest_plugin/api/base.py
+++ b/neutron_tempest_plugin/api/base.py
@@ -1466,6 +1466,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,
@@ -1517,11 +1521,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
@@ -1541,7 +1546,7 @@
             resources_ = self._extract_resources(body)
             self.assertEqual(1, len(resources_))
             resources.extend(resources_)
-        return resources
+        return self._test_resources(resources)
 
     @_require_pagination
     @_require_sorting
@@ -1565,7 +1570,7 @@
             )
             resources_ = self._extract_resources(body)
             self.assertEqual(1, len(resources_))
-            resources.extend(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'])
@@ -1583,7 +1588,7 @@
             )
             resources_ = self._extract_resources(body)
             self.assertEqual(1, len(resources_))
-            resources2.extend(resources_)
+            resources2.extend(self._test_resources(resources_))
 
         self.assertSameOrder(resources, reversed(resources2))
 
@@ -1603,14 +1608,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:
@@ -1622,7 +1628,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_router_interface_fip.py b/neutron_tempest_plugin/api/test_router_interface_fip.py
index 5d8ab67..dcaa17c 100644
--- a/neutron_tempest_plugin/api/test_router_interface_fip.py
+++ b/neutron_tempest_plugin/api/test_router_interface_fip.py
@@ -83,7 +83,7 @@
                                fip2['revision_number'])
         # NOTE(yamamoto): The status can be updated asynchronously.
         fip2_shown = self.client.show_floatingip(fip2['id'])['floatingip']
-        if 'revision_number' in fip2:
+        if 'revision_number' in fip2_shown:
             self.assertGreaterEqual(fip2_shown['revision_number'],
                                     fip2_updated['revision_number'])
         fip2_shown.pop('status')
diff --git a/neutron_tempest_plugin/scenario/test_metadata.py b/neutron_tempest_plugin/scenario/test_metadata.py
index af6bd09..239f6bc 100644
--- a/neutron_tempest_plugin/scenario/test_metadata.py
+++ b/neutron_tempest_plugin/scenario/test_metadata.py
@@ -12,7 +12,10 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+import base64
 import collections
+import textwrap
+import time
 
 from neutron_lib import constants as nlib_const
 from oslo_log import log as logging
@@ -32,6 +35,8 @@
 Server = collections.namedtuple(
     'Server', ['floating_ip', 'server', 'ssh_client'])
 
+QUERY_MSG = 'Queried the metadata service over IPv6'
+
 
 class MetadataTest(base.BaseTempestTestCase):
 
@@ -86,19 +91,49 @@
                                 security_groups=[self.security_group['id']],
                                 **params)
 
-    def _create_server(self, port, use_advanced_image=False, **params):
+    def _create_server(self, port=None, network_id=None,
+                       use_advanced_image=False, **params):
         if use_advanced_image:
             flavor_ref = CONF.neutron_plugin_options.advanced_image_flavor_ref
             image_ref = CONF.neutron_plugin_options.advanced_image_ref
         else:
             flavor_ref = CONF.compute.flavor_ref
             image_ref = CONF.compute.image_ref
+        if port:
+            networks = [{'port': port['id']}]
+        else:
+            networks = [{'uuid': network_id}]
         return self.create_server(flavor_ref=flavor_ref,
                                   image_ref=image_ref,
                                   key_name=self.keypair['name'],
-                                  networks=[{'port': port['id']}],
+                                  networks=networks,
                                   **params)['server']
 
+    def _get_metadata_query_script(self):
+        sheebang_line = '\n#!/bin/bash'
+        curl_cmd = '\ncurl http://[%(address)s' % {'address':
+                                                   nlib_const.METADATA_V6_IP}
+        ip_cmd = ("%$(ip -6 -br address show scope link up | head -1 | "
+                  "cut -d ' ' -f1)]/openstack/")
+        echo_cmd = '\necho %s' % QUERY_MSG
+        script = '%s%s%s%s' % (sheebang_line, curl_cmd, ip_cmd, echo_cmd)
+        script_clean = textwrap.dedent(script).lstrip().encode('utf8')
+        script_b64 = base64.b64encode(script_clean)
+        return {'user_data': script_b64}
+
+    def _wait_for_metadata_query_msg(self, vm):
+        timeout = 300
+        start_time = int(time.time())
+        while int(time.time()) - start_time < timeout:
+            console_output = self.os_primary.servers_client.get_console_output(
+                vm['id'])['output']
+            pos = console_output.find(QUERY_MSG)
+            if pos > -1:
+                return console_output, pos
+            time.sleep(30)
+        self.fail('Failed to find metadata query message in console log %s' %
+                  console_output)
+
     def _create_ssh_client(self, floating_ip, use_advanced_image=False):
         if use_advanced_image:
             username = CONF.neutron_plugin_options.advanced_image_ssh_user
@@ -153,3 +188,28 @@
         except exceptions.SSHExecCommandFailed:
             self._log_console_output()
             self._log_local_network_status()
+
+    @testtools.skipUnless(
+        CONF.neutron_plugin_options.advanced_image_ref or
+        CONF.neutron_plugin_options.default_image_is_advanced,
+        'Advanced image is required to run this test.')
+    @testtools.skipUnless(
+        CONF.neutron_plugin_options.firewall_driver == 'ovn',
+        "OVN driver is required to run this test - "
+        " while LP#2076916 is fixed")
+    @decorators.idempotent_id('7542892a-d132-471c-addb-172dcf888ff6')
+    def test_metadata_ipv6_only_network(self):
+        ipv6_network = self.create_network()
+        self.create_subnet(network=ipv6_network, ip_version=6,
+                           ipv6_ra_mode="slaac", ipv6_address_mode="slaac")
+        use_advanced_image = (
+            not CONF.neutron_plugin_options.default_image_is_advanced)
+        params = self._get_metadata_query_script()
+        params['config_drive'] = True
+        vm = self._create_server(
+            network_id=ipv6_network['id'],
+            use_advanced_image=use_advanced_image, **params)
+        self.wait_for_server_active(server=vm)
+        self.wait_for_guest_os_ready(vm)
+        console_output, pos = self._wait_for_metadata_query_msg(vm)
+        self.assertIn('latest', console_output[pos - 100:])