Merge "Added new API test_sg_rules_quota_values."
diff --git a/neutron_tempest_plugin/api/test_subnetpools_negative.py b/neutron_tempest_plugin/api/test_subnetpools_negative.py
index 214a012..5513c86 100644
--- a/neutron_tempest_plugin/api/test_subnetpools_negative.py
+++ b/neutron_tempest_plugin/api/test_subnetpools_negative.py
@@ -173,6 +173,12 @@
     @decorators.idempotent_id('3396ec6c-cb80-4ebe-b897-84e904580bdf')
     @utils.requires_ext(extension='address-scope', service='network')
     def test_tenant_create_subnetpool_associate_shared_address_scope(self):
+        # TODO(imalinovskiy): This test is temporary disabled
+        # to be able to test & merge
+        # https://review.opendev.org/709122/ and will be enabled again in
+        # https://review.opendev.org/711610/
+        self.skipTest("Temporary disabled")
+
         address_scope = self.create_address_scope(
             name=data_utils.rand_name('smoke-address-scope'), is_admin=True,
             shared=True, ip_version=4)
diff --git a/neutron_tempest_plugin/api/test_trunk.py b/neutron_tempest_plugin/api/test_trunk.py
index 823a95d..1f83bd8 100644
--- a/neutron_tempest_plugin/api/test_trunk.py
+++ b/neutron_tempest_plugin/api/test_trunk.py
@@ -235,7 +235,9 @@
                               'segmentation_id': segmentation_id2}]
 
         # Validate that subport got segmentation details from the network
-        self.assertEqual(expected_subports, trunk['sub_ports'])
+        self.assertEqual(
+            sorted(expected_subports, key=lambda subport: subport['port_id']),
+            sorted(trunk['sub_ports'], key=lambda subport: subport['port_id']))
 
 
 class TrunkTestMtusJSONBase(TrunkTestJSONBase):
diff --git a/neutron_tempest_plugin/common/utils.py b/neutron_tempest_plugin/common/utils.py
index c8ff194..34e7464 100644
--- a/neutron_tempest_plugin/common/utils.py
+++ b/neutron_tempest_plugin/common/utils.py
@@ -117,6 +117,14 @@
         pass
 
 
+def process_is_running(ssh_client, process_name):
+    try:
+        ssh_client.exec_command("pidof %s" % process_name)
+        return True
+    except exceptions.SSHExecCommandFailed:
+        return False
+
+
 def spawn_http_server(ssh_client, port, message):
     cmd = ("(echo -e 'HTTP/1.1 200 OK\r\n'; echo '%(msg)s') "
            "| sudo nc -lp %(port)d &" % {'msg': message, 'port': port})
diff --git a/neutron_tempest_plugin/scenario/base.py b/neutron_tempest_plugin/scenario/base.py
index fa91b31..9dd830c 100644
--- a/neutron_tempest_plugin/scenario/base.py
+++ b/neutron_tempest_plugin/scenario/base.py
@@ -31,6 +31,7 @@
 from neutron_tempest_plugin.common import ip as ip_utils
 from neutron_tempest_plugin.common import shell
 from neutron_tempest_plugin.common import ssh
+from neutron_tempest_plugin.common import utils
 from neutron_tempest_plugin import config
 from neutron_tempest_plugin import exceptions
 from neutron_tempest_plugin.scenario import constants
@@ -53,16 +54,19 @@
     return distutils.version.StrictVersion(m.group(1) if m else '7.60')
 
 
-def get_ncat_server_cmd(port, protocol, msg):
+def get_ncat_server_cmd(port, protocol, msg=None):
     udp = ''
     if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
         udp = '-u'
     cmd = "nc %(udp)s -p %(port)s -lk " % {
         'udp': udp, 'port': port}
-    if CONF.neutron_plugin_options.default_image_is_advanced:
-        cmd += "-c 'echo %s' &" % msg
+    if msg:
+        if CONF.neutron_plugin_options.default_image_is_advanced:
+            cmd += "-c 'echo %s' &" % msg
+        else:
+            cmd += "-e echo %s &" % msg
     else:
-        cmd += "-e echo %s &" % msg
+        cmd += "< /dev/zero &"
     return cmd
 
 
@@ -468,7 +472,20 @@
                 self._log_console_output(servers)
             raise
 
-    def nc_listen(self, server, ssh_client, port, protocol, echo_msg):
+    def ensure_nc_listen(self, ssh_client, port, protocol, echo_msg=None,
+                         servers=None):
+        """Ensure that nc server listening on the given TCP/UDP port is up.
+
+        Listener is created always on remote host.
+        """
+        def spawn_and_check_process():
+            self.nc_listen(ssh_client, port, protocol, echo_msg, servers)
+            return utils.process_is_running(ssh_client, "nc")
+
+        utils.wait_until_true(spawn_and_check_process)
+
+    def nc_listen(self, ssh_client, port, protocol, echo_msg=None,
+                  servers=None):
         """Create nc server listening on the given TCP/UDP port.
 
         Listener is created always on remote host.
@@ -479,7 +496,7 @@
                 become_root=True)
         except lib_exc.SSHTimeout as ssh_e:
             LOG.debug(ssh_e)
-            self._log_console_output([server])
+            self._log_console_output(servers)
             raise
 
     def nc_client(self, ip_address, port, protocol):
diff --git a/neutron_tempest_plugin/scenario/test_port_forwardings.py b/neutron_tempest_plugin/scenario/test_port_forwardings.py
index 2d77b65..ab04050 100644
--- a/neutron_tempest_plugin/scenario/test_port_forwardings.py
+++ b/neutron_tempest_plugin/scenario/test_port_forwardings.py
@@ -83,11 +83,11 @@
     def _test_udp_port_forwarding(self, servers):
 
         def _message_received(server, ssh_client, expected_msg):
-            self.nc_listen(server,
-                           ssh_client,
+            self.nc_listen(ssh_client,
                            server['port_forwarding_udp']['internal_port'],
                            constants.PROTO_NAME_UDP,
-                           expected_msg)
+                           expected_msg,
+                           [server])
             received_msg = self.nc_client(
                 self.fip['floating_ip_address'],
                 server['port_forwarding_udp']['external_port'],
diff --git a/neutron_tempest_plugin/scenario/test_qos.py b/neutron_tempest_plugin/scenario/test_qos.py
index 7540741..938d2b0 100644
--- a/neutron_tempest_plugin/scenario/test_qos.py
+++ b/neutron_tempest_plugin/scenario/test_qos.py
@@ -81,8 +81,7 @@
 
     def _check_bw(self, ssh_client, host, port, expected_bw=LIMIT_BYTES_SEC):
         utils.kill_nc_process(ssh_client)
-        cmd = ("(nc -ll -p %d < /dev/zero > /dev/null &)" % port)
-        ssh_client.exec_command(cmd, timeout=5)
+        self.ensure_nc_listen(ssh_client, port, "tcp")
 
         # Open TCP socket to remote VM and download big file
         start_time = time.time()
diff --git a/neutron_tempest_plugin/scenario/test_trunk.py b/neutron_tempest_plugin/scenario/test_trunk.py
index 7fd6c52..585af06 100644
--- a/neutron_tempest_plugin/scenario/test_trunk.py
+++ b/neutron_tempest_plugin/scenario/test_trunk.py
@@ -39,7 +39,7 @@
 
 
 class TrunkTest(base.BaseTempestTestCase):
-    credentials = ['primary']
+    credentials = ['primary', 'admin']
     force_tenant_isolation = False
 
     @classmethod
@@ -279,12 +279,73 @@
             should_succeed=False)
 
         # allow intra-security-group traffic
-        self.create_pingable_secgroup_rule(self.security_group['id'])
+        sg_rule = self.create_pingable_secgroup_rule(self.security_group['id'])
+        self.addCleanup(
+                self.os_primary.network_client.delete_security_group_rule,
+                sg_rule['id'])
         self.check_remote_connectivity(
             vm1.ssh_client,
             vm2.subport['fixed_ips'][0]['ip_address'],
             servers=[vm1, vm2])
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
+                          'Cold migration is not available.')
+    @testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
+                          'Less than 2 compute nodes, skipping multinode '
+                          'tests.')
+    @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.")
+    @decorators.attr(type='slow')
+    @decorators.idempotent_id('ecd7de30-1c90-4280-b97c-1bed776d5d07')
+    def test_trunk_vm_migration(self):
+        '''Test connectivity after migration of the server with trunk
+
+        A successfully migrated server shows a VERIFY_RESIZE status that
+        requires confirmation. Need to reconfigure VLAN interface on server
+        side after migration is finished as the configuration doesn't survive
+        the reboot.
+        '''
+        vlan_tag = 10
+        vlan_network = self.create_network()
+        vlan_subnet = self.create_subnet(vlan_network)
+        sg_rule = self.create_pingable_secgroup_rule(self.security_group['id'])
+        self.addCleanup(
+                self.os_primary.network_client.delete_security_group_rule,
+                sg_rule['id'])
+
+        use_advanced_image = (
+            not CONF.neutron_plugin_options.default_image_is_advanced)
+        servers = {}
+        for role in ['migrate', 'connection_test']:
+            servers[role] = self._create_server_with_trunk_port(
+                                subport_network=vlan_network,
+                                segmentation_id=vlan_tag,
+                                use_advanced_image=use_advanced_image)
+        for role in ['migrate', 'connection_test']:
+            self.wait_for_server_active(servers[role].server)
+            self._configure_vlan_subport(vm=servers[role],
+                                         vlan_tag=vlan_tag,
+                                         vlan_subnet=vlan_subnet)
+
+        self.check_remote_connectivity(
+                servers['connection_test'].ssh_client,
+                servers['migrate'].subport['fixed_ips'][0]['ip_address'])
+
+        client = self.os_admin.compute.ServersClient()
+        client.migrate_server(servers['migrate'].server['id'])
+        self.wait_for_server_status(servers['migrate'].server,
+                                    'VERIFY_RESIZE')
+        client.confirm_resize_server(servers['migrate'].server['id'])
+        self._configure_vlan_subport(vm=servers['migrate'],
+                                     vlan_tag=vlan_tag,
+                                     vlan_subnet=vlan_subnet)
+
+        self.check_remote_connectivity(
+                servers['connection_test'].ssh_client,
+                servers['migrate'].subport['fixed_ips'][0]['ip_address'])
+
     @testtools.skipUnless(
         (CONF.neutron_plugin_options.advanced_image_ref or
          CONF.neutron_plugin_options.default_image_is_advanced),
@@ -318,7 +379,10 @@
             self.wait_for_server_active(vm.server)
 
         # allow ICMP traffic
-        self.create_pingable_secgroup_rule(self.security_group['id'])
+        sg_rule = self.create_pingable_secgroup_rule(self.security_group['id'])
+        self.addCleanup(
+                self.os_primary.network_client.delete_security_group_rule,
+                sg_rule['id'])
 
         # Ping from trunk_network_server to normal_network_server
         # via parent port