Merge "Fix neutron-dynamic-routing tests when DVR is disabled"
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/api/test_bgp_speaker_extensions.py b/neutron_tempest_plugin/neutron_dynamic_routing/api/test_bgp_speaker_extensions.py
index fe213ee..6720c22 100644
--- a/neutron_tempest_plugin/neutron_dynamic_routing/api/test_bgp_speaker_extensions.py
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/api/test_bgp_speaker_extensions.py
@@ -37,7 +37,7 @@
     # The disable_ssl appears in identity
     disable_ssl_certificate_validation = (
         CONF.identity.disable_ssl_certificate_validation)
-    ca_certs = None
+    ca_certs = CONF.identity.ca_certificates_file
 
     # Trace in debug section
     trace_requests = CONF.debug.trace_requests
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base.py b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base.py
index de8677f..f752436 100644
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base.py
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base.py
@@ -54,7 +54,7 @@
     # The disable_ssl appears in identity
     disable_ssl_certificate_validation = (
         CONF.identity.disable_ssl_certificate_validation)
-    ca_certs = None
+    ca_certs = CONF.identity.ca_certificates_file
 
     # Trace in debug section
     trace_requests = CONF.debug.trace_requests
diff --git a/neutron_tempest_plugin/scenario/base.py b/neutron_tempest_plugin/scenario/base.py
index 35e5c31..9223893 100644
--- a/neutron_tempest_plugin/scenario/base.py
+++ b/neutron_tempest_plugin/scenario/base.py
@@ -62,11 +62,10 @@
         'udp': udp, 'port': port}
     if msg:
         if CONF.neutron_plugin_options.default_image_is_advanced:
-            cmd += "-c 'echo %s' &" % msg
+            cmd += "-c 'echo %s' " % msg
         else:
-            cmd += "-e echo %s &" % msg
-    else:
-        cmd += "< /dev/zero &"
+            cmd += "-e echo %s " % msg
+    cmd += "< /dev/zero &{0}sleep 0.1{0}".format('\n')
     return cmd
 
 
@@ -446,11 +445,13 @@
         self.wait_for_server_status(
             server, constants.SERVER_STATUS_ACTIVE, client)
 
-    def check_servers_hostnames(self, servers, log_errors=True):
+    def check_servers_hostnames(self, servers, timeout=None, log_errors=True):
         """Compare hostnames of given servers with their names."""
         try:
             for server in servers:
                 kwargs = {}
+                if timeout:
+                    kwargs['timeout'] = timeout
                 try:
                     kwargs['port'] = (
                         server['port_forwarding_tcp']['external_port'])
@@ -495,7 +496,7 @@
         try:
             return ssh_client.execute_script(
                 get_ncat_server_cmd(port, protocol, echo_msg),
-                become_root=True)
+                become_root=True, combine_stderr=True)
         except lib_exc.SSHTimeout as ssh_e:
             LOG.debug(ssh_e)
             self._log_console_output(servers)
diff --git a/neutron_tempest_plugin/scenario/test_port_forwardings.py b/neutron_tempest_plugin/scenario/test_port_forwardings.py
index ab04050..da1db1b 100644
--- a/neutron_tempest_plugin/scenario/test_port_forwardings.py
+++ b/neutron_tempest_plugin/scenario/test_port_forwardings.py
@@ -17,6 +17,7 @@
 from oslo_log import log
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 from neutron_tempest_plugin.common import ssh
 from neutron_tempest_plugin.common import utils
@@ -45,9 +46,9 @@
         cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
         cls.keypair = cls.create_keypair()
 
-    def _prepare_resources(self, num_servers, internal_tcp_port, protocol):
+    def _prepare_resources(self, num_servers, internal_tcp_port, protocol,
+                           external_port_base=1025):
         servers = []
-        external_port_base = 1025
         for i in range(1, num_servers + 1):
             internal_udp_port = internal_tcp_port + 10
             external_tcp_port = external_port_base + i
@@ -121,3 +122,65 @@
         self.check_servers_hostnames(servers)
         # And now test UDP port forwarding using nc
         self._test_udp_port_forwarding(servers)
+
+    @decorators.idempotent_id('aa19d46c-a4a6-11ea-bb37-0242ac130002')
+    def test_port_forwarding_editing_and_deleting_tcp_rule(self):
+        server = self._prepare_resources(
+            num_servers=1, internal_tcp_port=22,
+            protocol=constants.PROTO_NAME_TCP,
+            external_port_base=1035)
+        fip_id = server[0]['port_forwarding_tcp']['floatingip_id']
+        pf_id = server[0]['port_forwarding_tcp']['id']
+
+        # Check connectivity with the original parameters
+        self.check_servers_hostnames(server)
+
+        # Use a reasonable timeout to verify that connections will not
+        # happen. Default would be 196 seconds, which is an overkill.
+        test_ssh_connect_timeout = 6
+
+        # Update external port and check connectivity with original parameters
+        # Port under server[0]['port_forwarding_tcp']['external_port'] should
+        # not answer at this point.
+
+        def fip_pf_connectivity():
+            try:
+                self.check_servers_hostnames(
+                    server, timeout=test_ssh_connect_timeout)
+                return True
+            except (AssertionError, lib_exc.SSHTimeout):
+                return False
+
+        def no_fip_pf_connectivity():
+            return not fip_pf_connectivity()
+
+        self.client.update_port_forwarding(fip_id, pf_id, external_port=3333)
+        utils.wait_until_true(
+            no_fip_pf_connectivity,
+            exception=RuntimeError(
+                "Connection to the server {!r} through "
+                "port {!r} is still possible.".format(
+                    server[0]['id'],
+                    server[0]['port_forwarding_tcp']['external_port'])))
+
+        # Check connectivity with the new parameters
+        server[0]['port_forwarding_tcp']['external_port'] = 3333
+        utils.wait_until_true(
+            fip_pf_connectivity,
+            exception=RuntimeError(
+                "Connection to the server {!r} through "
+                "port {!r} is not possible.".format(
+                    server[0]['id'],
+                    server[0]['port_forwarding_tcp']['external_port'])))
+
+        # Remove port forwarding and ensure connection stops working.
+        self.client.delete_port_forwarding(fip_id, pf_id)
+        self.assertRaises(lib_exc.NotFound, self.client.get_port_forwarding,
+                          fip_id, pf_id)
+        utils.wait_until_true(
+            no_fip_pf_connectivity,
+            exception=RuntimeError(
+                "Connection to the server {!r} through "
+                "port {!r} is still possible.".format(
+                    server[0]['id'],
+                    server[0]['port_forwarding_tcp']['external_port'])))
diff --git a/neutron_tempest_plugin/scenario/test_security_groups.py b/neutron_tempest_plugin/scenario/test_security_groups.py
index dc14857..23a5224 100644
--- a/neutron_tempest_plugin/scenario/test_security_groups.py
+++ b/neutron_tempest_plugin/scenario/test_security_groups.py
@@ -400,3 +400,76 @@
                 ssh_clients[0],
                 ssh_clients[2],
                 test_ip, port)
+
+    @decorators.idempotent_id('f07d0159-8f9e-4faa-87f5-a869ab0ad490')
+    def test_intra_sg_isolation(self):
+        """Test intra security group isolation
+
+        This test creates a security group that does not allow ingress
+        packets from vms of the same security group. The purpose of this
+        test is to verify that intra SG traffic is properly blocked, while
+        traffic like metadata and DHCP remains working due to the
+        allow-related behavior of the egress rules (added via default).
+        """
+        # create a security group and make it loginable
+        secgrp_name = data_utils.rand_name('secgrp')
+        secgrp = self.os_primary.network_client.create_security_group(
+            name=secgrp_name)
+        secgrp_id = secgrp['security_group']['id']
+        # add security group to cleanup
+        self.security_groups.append(secgrp['security_group'])
+
+        # remove all rules and add ICMP, DHCP and metadata as egress,
+        # and ssh as ingress.
+        for sgr in secgrp['security_group']['security_group_rules']:
+            self.client.delete_security_group_rule(sgr['id'])
+
+        self.create_loginable_secgroup_rule(secgroup_id=secgrp_id)
+        rule_list = [{'direction': constants.EGRESS_DIRECTION,
+                      'protocol': constants.PROTO_NAME_TCP,
+                      'remote_ip_prefix': '169.254.169.254/32',
+                      'description': 'metadata out',
+                      },
+                     {'direction': constants.EGRESS_DIRECTION,
+                      'protocol': constants.PROTO_NAME_UDP,
+                      'port_range_min': '67',
+                      'port_range_max': '67',
+                      'description': 'dhcpv4 out',
+                      },
+                     {'direction': constants.EGRESS_DIRECTION,
+                      'protocol': constants.PROTO_NAME_ICMP,
+                      'description': 'ping out',
+                      },
+                     ]
+        self.create_secgroup_rules(rule_list, secgroup_id=secgrp_id)
+
+        # go vms, go!
+        ssh_clients, fips, servers = self.create_vm_testing_sec_grp(
+            num_servers=2, security_groups=[{'name': secgrp_name}])
+
+        # verify SSH functionality. This will ensure that servers were
+        # able to reach dhcp + metadata servers
+        for fip in fips:
+            self.check_connectivity(fip['floating_ip_address'],
+                                    CONF.validation.image_ssh_user,
+                                    self.keypair['private_key'])
+
+        # try to ping instances without intra SG permission (should fail)
+        self.check_remote_connectivity(
+            ssh_clients[0], fips[1]['fixed_ip_address'],
+            should_succeed=False)
+        self.check_remote_connectivity(
+            ssh_clients[1], fips[0]['fixed_ip_address'],
+            should_succeed=False)
+
+        # add intra sg rule. This will allow packets from servers that
+        # are in the same sg
+        rule_list = [{'direction': constants.INGRESS_DIRECTION,
+                      'remote_group_id': secgrp_id}]
+        self.create_secgroup_rules(rule_list, secgroup_id=secgrp_id)
+
+        # try to ping instances with intra SG permission
+        self.check_remote_connectivity(
+            ssh_clients[0], fips[1]['fixed_ip_address'])
+        self.check_remote_connectivity(
+            ssh_clients[1], fips[0]['fixed_ip_address'])