diff --git a/neutron_tempest_plugin/config.py b/neutron_tempest_plugin/config.py index 2290d0f3..38123836 100644 --- a/neutron_tempest_plugin/config.py +++ b/neutron_tempest_plugin/config.py @@ -68,6 +68,11 @@ NeutronPluginOptions = [ default=None, choices=['None', 'linuxbridge', 'ovs', 'sriov'], help='Agent used for devstack@q-agt.service'), + cfg.StrOpt('firewall_driver', + default=None, + choices=['None', 'openvswitch', 'ovn', + 'iptables_hybrid', 'iptables'], + help='Driver for security groups firewall in the L2 agent'), # Multicast tests settings cfg.StrOpt('multicast_group_range', diff --git a/neutron_tempest_plugin/scenario/base.py b/neutron_tempest_plugin/scenario/base.py index 6910c11a..334d5438 100644 --- a/neutron_tempest_plugin/scenario/base.py +++ b/neutron_tempest_plugin/scenario/base.py @@ -158,13 +158,14 @@ class BaseTempestTestCase(base_api.BaseNetworkTest): if sg['name'] == constants.DEFAULT_SECURITY_GROUP: secgroup_id = sg['id'] break - + resp = [] for rule in rule_list: direction = rule.pop('direction') - client.create_security_group_rule( - direction=direction, - security_group_id=secgroup_id, - **rule) + resp.append(client.create_security_group_rule( + direction=direction, + security_group_id=secgroup_id, + **rule)['security_group_rule']) + return resp @classmethod def create_loginable_secgroup_rule(cls, secgroup_id=None, diff --git a/neutron_tempest_plugin/scenario/test_security_groups.py b/neutron_tempest_plugin/scenario/test_security_groups.py index 9059a2f7..8b7098ee 100644 --- a/neutron_tempest_plugin/scenario/test_security_groups.py +++ b/neutron_tempest_plugin/scenario/test_security_groups.py @@ -12,8 +12,12 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from neutron_lib import constants +import netaddr +from neutron_lib import constants +import testtools + +from tempest.common import utils as tempest_utils from tempest.common import waiters from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils @@ -310,6 +314,95 @@ class NetworkSecGroupTest(base.BaseTempestTestCase): self.ping_ip_address(fips[0]['floating_ip_address'], should_succeed=False) + @testtools.skipUnless( + CONF.neutron_plugin_options.firewall_driver == 'openvswitch', + "Openvswitch agent is required to run this test") + @decorators.idempotent_id('678dd4c0-2953-4626-b89c-8e7e4110ec4b') + @tempest_utils.requires_ext(extension="address-group", service="network") + @tempest_utils.requires_ext( + extension="security-groups-remote-address-group", service="network") + def test_remote_group_and_remote_address_group(self): + """Test SG rules with remote group and remote address group + + This test checks the ICMP connection among two servers using a security + group rule with remote group and another rule with remote address + group. The connection should be granted when at least one of the rules + is applied. When both rules are applied (overlapped), removing one of + them should not disable the connection. + """ + # create a new sec group + ssh_secgrp_name = data_utils.rand_name('ssh_secgrp') + ssh_secgrp = self.os_primary.network_client.create_security_group( + name=ssh_secgrp_name) + # add cleanup + self.security_groups.append(ssh_secgrp['security_group']) + # configure sec group to support SSH connectivity + self.create_loginable_secgroup_rule( + secgroup_id=ssh_secgrp['security_group']['id']) + # spawn two instances with the sec group created + server_ssh_clients, fips, servers = self.create_vm_testing_sec_grp( + security_groups=[{'name': ssh_secgrp_name}]) + # verify SSH functionality + for i in range(2): + self.check_connectivity(fips[i]['floating_ip_address'], + CONF.validation.image_ssh_user, + self.keypair['private_key']) + # try to ping instances without ICMP permissions + self.check_remote_connectivity( + server_ssh_clients[0], fips[1]['fixed_ip_address'], + should_succeed=False) + # add ICMP support to the remote group + rule_list = [{'protocol': constants.PROTO_NUM_ICMP, + 'direction': constants.INGRESS_DIRECTION, + 'remote_group_id': ssh_secgrp['security_group']['id']}] + remote_sg_rid = self.create_secgroup_rules( + rule_list, secgroup_id=ssh_secgrp['security_group']['id'])[0]['id'] + # verify ICMP connectivity between instances works + self.check_remote_connectivity( + server_ssh_clients[0], fips[1]['fixed_ip_address'], + servers=servers) + # make sure ICMP connectivity doesn't work from framework + self.ping_ip_address(fips[0]['floating_ip_address'], + should_succeed=False) + + # add ICMP rule with remote address group + test_ag = self.create_address_group( + name=data_utils.rand_name('test_ag'), + addresses=[str(netaddr.IPNetwork(fips[0]['fixed_ip_address']))]) + rule_list = [{'protocol': constants.PROTO_NUM_ICMP, + 'direction': constants.INGRESS_DIRECTION, + 'remote_address_group_id': test_ag['id']}] + remote_ag_rid = self.create_secgroup_rules( + rule_list, secgroup_id=ssh_secgrp['security_group']['id'])[0]['id'] + # verify ICMP connectivity between instances still works + self.check_remote_connectivity( + server_ssh_clients[0], fips[1]['fixed_ip_address'], + servers=servers) + # make sure ICMP connectivity doesn't work from framework + self.ping_ip_address(fips[0]['floating_ip_address'], + should_succeed=False) + + # Remove the ICMP rule with remote group + self.client.delete_security_group_rule(remote_sg_rid) + # verify ICMP connectivity between instances still works as granted + # by the rule with remote address group + self.check_remote_connectivity( + server_ssh_clients[0], fips[1]['fixed_ip_address'], + servers=servers) + # make sure ICMP connectivity doesn't work from framework + self.ping_ip_address(fips[0]['floating_ip_address'], + should_succeed=False) + + # Remove the ICMP rule with remote address group + self.client.delete_security_group_rule(remote_ag_rid) + # verify ICMP connectivity between instances doesn't work now + self.check_remote_connectivity( + server_ssh_clients[0], fips[1]['fixed_ip_address'], + should_succeed=False) + # make sure ICMP connectivity doesn't work from framework + self.ping_ip_address(fips[0]['floating_ip_address'], + should_succeed=False) + @decorators.idempotent_id('f07d0159-8f9e-4faa-87f5-a869ab0ad488') def test_multiple_ports_secgroup_inheritance(self): """Test multiple port security group inheritance diff --git a/zuul.d/master_jobs.yaml b/zuul.d/master_jobs.yaml index 0d197e03..207a4f19 100644 --- a/zuul.d/master_jobs.yaml +++ b/zuul.d/master_jobs.yaml @@ -64,6 +64,7 @@ - router-admin-state-down-before-update - router_availability_zone - security-group + - security-groups-remote-address-group - segment - service-type - sorting @@ -162,6 +163,7 @@ available_features: "{{ network_available_features | join(',') }}" neutron_plugin_options: available_type_drivers: flat,vlan,local,vxlan + firewall_driver: openvswitch irrelevant-files: &openvswitch-scenario-irrelevant-files - ^(test-|)requirements.txt$ - ^releasenotes/.*$ @@ -231,6 +233,7 @@ available_features: "{{ network_available_features | join(',') }}" neutron_plugin_options: available_type_drivers: flat,vlan,local,vxlan + firewall_driver: iptables_hybrid irrelevant-files: - ^(test-|)requirements.txt$ - ^releasenotes/.*$ @@ -306,6 +309,7 @@ neutron_plugin_options: available_type_drivers: flat,vlan,local,vxlan q_agent: linuxbridge + firewall_driver: iptables irrelevant-files: - ^(test-|)requirements.txt$ - ^releasenotes/.*$ @@ -403,6 +407,7 @@ neutron_plugin_options: available_type_drivers: local,flat,vlan,geneve is_igmp_snooping_enabled: True + firewall_driver: ovn irrelevant-files: - ^(test-|)requirements.txt$ - ^releasenotes/.*$ @@ -543,6 +548,7 @@ image_is_advanced: true available_type_drivers: flat,geneve,vlan,gre,local,vxlan l3_agent_mode: dvr_snat + firewall_driver: openvswitch group-vars: subnode: devstack_services: