Merge "[FWaaS] Add basic scenario test for FW attached to L2 ports"
This commit is contained in:
commit
570da83228
4 files changed
+263
-60
No files matched your search
@@ -89,6 +89,26 @@ class FWaaSClientMixin:
|
||||
fwg['id'])
|
||||
return fwg
|
||||
|
||||
def create_firewall_group_and_wait(self, ports=None, **kwargs):
|
||||
fw_group = self.create_firewall_group(ports=ports or [], **kwargs)
|
||||
self.addCleanup(self.update_firewall_group_and_wait, fw_group['id'],
|
||||
ports=[])
|
||||
self._wait_firewall_group_active(fw_group['id'])
|
||||
return fw_group
|
||||
|
||||
def _assert_firewall_group_active(self, firewall_group_id):
|
||||
fwg_body = self.firewall_groups_client.show_firewall_group(
|
||||
firewall_group_id)['firewall_group']
|
||||
self.assertEqual(nl_constants.ACTIVE, fwg_body['status'],
|
||||
'Firewall group failed to apply on agent')
|
||||
|
||||
def _wait_firewall_group_active(self, firewall_group_id):
|
||||
self._wait_firewall_group_while(
|
||||
firewall_group_id,
|
||||
[nl_constants.PENDING_CREATE, nl_constants.PENDING_UPDATE,
|
||||
nl_constants.INACTIVE])
|
||||
self._assert_firewall_group_active(firewall_group_id)
|
||||
|
||||
def delete_firewall_group_and_wait(self, firewall_group_id):
|
||||
self.firewall_groups_client.delete_firewall_group(firewall_group_id)
|
||||
self._wait_firewall_group_while(firewall_group_id,
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
from tempest import config
|
||||
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
from neutron_tempest_plugin.common import ssh
|
||||
@@ -21,9 +23,13 @@ from neutron_tempest_plugin.fwaas.common import fwaas_v2_client
|
||||
from neutron_tempest_plugin.scenario import base as scenario_base
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_FWG = 'default'
|
||||
|
||||
|
||||
class FWaaSScenarioTestBase:
|
||||
|
||||
def check_ssh_connectivity(self, ip_address, username=None,
|
||||
private_key=None, should_connect=True):
|
||||
"""Check SSH reachability, including expected negative checks."""
|
||||
@@ -43,9 +49,89 @@ class FWaaSScenarioTestBase:
|
||||
else:
|
||||
self.assertTrue(should_connect, "Unexpectedly reachable")
|
||||
|
||||
def _create_server(self, network, security_group=None):
|
||||
keys = self.create_keypair()
|
||||
port_kwargs = {}
|
||||
if security_group is not None:
|
||||
port_kwargs['security_groups'] = [security_group['id']]
|
||||
port = self.create_port(network, **port_kwargs)
|
||||
server_kwargs = {
|
||||
'flavor_ref': CONF.compute.flavor_ref,
|
||||
'image_ref': CONF.compute.image_ref,
|
||||
'key_name': keys['name'],
|
||||
'networks': [{'port': port['id']}]}
|
||||
if security_group is not None:
|
||||
server_kwargs['security_groups'] = [
|
||||
{'name': security_group['name']}]
|
||||
server = self.create_server(**server_kwargs)
|
||||
return server['server'], keys, port
|
||||
|
||||
def _check_server_connectivity(self, floating_ip, keys1, address_list,
|
||||
should_connect=True, ssh_source=None,
|
||||
servers=None):
|
||||
if ssh_source is None:
|
||||
ssh_source = ssh.Client(
|
||||
floating_ip['floating_ip_address'],
|
||||
CONF.validation.image_ssh_user,
|
||||
pkey=keys1)
|
||||
|
||||
for remote_ip in address_list:
|
||||
self.check_remote_connectivity(
|
||||
ssh_source, remote_ip, ping_count=1,
|
||||
should_succeed=should_connect,
|
||||
servers=servers)
|
||||
|
||||
def _create_network_subnet(self, prefix="smoke-",
|
||||
port_security_enabled=True):
|
||||
network = self.create_network(
|
||||
network_name="network-%s" % prefix,
|
||||
port_security_enabled=port_security_enabled)
|
||||
subnet = self.create_subnet(
|
||||
network=network, name="subnet-%s" % prefix)
|
||||
return network, subnet
|
||||
|
||||
def _create_router_with_external_gateway(self, namestart, subnet_id=None):
|
||||
router = self.create_router(
|
||||
router_name=namestart,
|
||||
admin_state_up=True,
|
||||
external_network_id=CONF.network.public_network_id)
|
||||
if subnet_id:
|
||||
self.create_router_interface(router['id'], subnet_id=subnet_id)
|
||||
return router
|
||||
|
||||
def _create_fip_access_policies(self):
|
||||
fw_allow_icmp_rule = self.create_firewall_rule(
|
||||
action="allow", protocol="icmp")
|
||||
fw_allow_ssh_rule = self.create_firewall_rule(
|
||||
action="allow", protocol="tcp", destination_port=22)
|
||||
fw_allow_egress_ssh_rule = self.create_firewall_rule(
|
||||
action="allow", protocol="tcp", source_port=22)
|
||||
fw_ingress_policy = self.create_firewall_policy(
|
||||
firewall_rules=[fw_allow_icmp_rule['id'], fw_allow_ssh_rule['id']])
|
||||
fw_egress_policy = self.create_firewall_policy(
|
||||
firewall_rules=[fw_allow_icmp_rule['id'],
|
||||
fw_allow_egress_ssh_rule['id']])
|
||||
return (fw_allow_icmp_rule, fw_allow_ssh_rule,
|
||||
fw_allow_egress_ssh_rule, fw_ingress_policy, fw_egress_policy)
|
||||
|
||||
def _get_default_firewall_group(self):
|
||||
fw_groups = self.firewall_groups_client.list_firewall_groups()[
|
||||
'firewall_groups']
|
||||
default_fwgs = [fwg for fwg in fw_groups if fwg['name'] == DEFAULT_FWG]
|
||||
return default_fwgs[0] if default_fwgs else None
|
||||
|
||||
def _dissociate_ports_from_default_firewall_group(self, port_ids):
|
||||
default_fwg = self._get_default_firewall_group()
|
||||
if default_fwg is None:
|
||||
return
|
||||
remaining_ports = [port_id for port_id in default_fwg.get('ports', [])
|
||||
if port_id not in port_ids]
|
||||
self.update_firewall_group_and_wait(default_fwg['id'],
|
||||
ports=remaining_ports)
|
||||
|
||||
|
||||
class FWaaSScenarioTest_V2(fwaas_v2_client.FWaaSClientMixin,
|
||||
FWaaSScenarioTestBase,
|
||||
scenario_base.BaseTempestTestCase):
|
||||
credentials = ['primary', 'admin']
|
||||
required_extensions = ['fwaas_v2', 'security-group', 'router']
|
||||
required_extensions = ['fwaas_v2', 'router']
|
||||
@@ -36,28 +36,13 @@ class TestFWaaS_v2(base.FWaaSScenarioTest_V2):
|
||||
- public_network_id
|
||||
"""
|
||||
|
||||
required_network_extensions = ['fwaas_v2', 'security-group', 'router']
|
||||
|
||||
def setUp(self):
|
||||
LOG.debug("Initializing FWaaSScenarioTest Setup")
|
||||
super().setUp()
|
||||
LOG.debug("FWaaSScenarioTest Setup done.")
|
||||
|
||||
def _create_server(self, network, security_group=None):
|
||||
keys = self.create_keypair()
|
||||
port_kwargs = {}
|
||||
if security_group is not None:
|
||||
port_kwargs['security_groups'] = [security_group['id']]
|
||||
port = self.create_port(network, **port_kwargs)
|
||||
server_kwargs = {
|
||||
'flavor_ref': CONF.compute.flavor_ref,
|
||||
'image_ref': CONF.compute.image_ref,
|
||||
'key_name': keys['name'],
|
||||
'networks': [{'port': port['id']}]}
|
||||
if security_group is not None:
|
||||
server_kwargs['security_groups'] = [
|
||||
{'name': security_group['name']}]
|
||||
server = self.create_server(**server_kwargs)
|
||||
return server['server'], keys, port
|
||||
|
||||
def _check_connectivity_between_internal_networks(
|
||||
self, floating_ip1, keys1, network2, server2, should_connect=True):
|
||||
internal_ips = (p['fixed_ips'][0]['ip_address'] for p in
|
||||
@@ -69,34 +54,9 @@ class TestFWaaS_v2(base.FWaaSScenarioTest_V2):
|
||||
floating_ip1, keys1, internal_ips, should_connect,
|
||||
servers=[server2])
|
||||
|
||||
def _check_server_connectivity(self, floating_ip, private_key,
|
||||
address_list, should_connect=True,
|
||||
servers=None):
|
||||
ssh_source = ssh.Client(
|
||||
floating_ip['floating_ip_address'],
|
||||
CONF.validation.image_ssh_user,
|
||||
pkey=private_key)
|
||||
|
||||
for remote_ip in address_list:
|
||||
self.check_remote_connectivity(
|
||||
ssh_source, remote_ip, should_succeed=should_connect,
|
||||
servers=servers)
|
||||
|
||||
def _create_network_subnet(self, prefix="smoke-",
|
||||
port_security_enabled=True):
|
||||
network_prefix = "network-%s" % prefix
|
||||
subnet_prefix = "subnet-%s" % prefix
|
||||
network = self.create_network(
|
||||
network_name=data_utils.rand_name(network_prefix),
|
||||
port_security_enabled=port_security_enabled)
|
||||
subnet = self.create_subnet(
|
||||
network=network, name=data_utils.rand_name(subnet_prefix))
|
||||
return network, subnet
|
||||
|
||||
def _create_test_server(self, network, security_group):
|
||||
def _create_test_server(self, network):
|
||||
pub_network_id = CONF.network.public_network_id
|
||||
server, keys, port = self._create_server(
|
||||
network, security_group=security_group)
|
||||
server, keys, port = self._create_server(network)
|
||||
private_key = keys['private_key']
|
||||
server_floating_ip = self.create_floatingip(
|
||||
external_network_id=pub_network_id, port=port)
|
||||
@@ -127,12 +87,14 @@ class TestFWaaS_v2(base.FWaaSScenarioTest_V2):
|
||||
LOG.debug('Starting Topology Creation')
|
||||
resp = {}
|
||||
# Create Network1 and Subnet1.
|
||||
network1, subnet1 = self._create_network_subnet()
|
||||
network1, subnet1 = self._create_network_subnet(
|
||||
port_security_enabled=False)
|
||||
resp['network1'] = network1
|
||||
resp['subnet1'] = subnet1
|
||||
|
||||
# Create Network2 and Subnet2.
|
||||
network2, subnet2 = self._create_network_subnet()
|
||||
network2, subnet2 = self._create_network_subnet(
|
||||
port_security_enabled=False)
|
||||
resp['network2'] = network2
|
||||
resp['subnet2'] = subnet2
|
||||
|
||||
@@ -154,16 +116,13 @@ class TestFWaaS_v2(base.FWaaSScenarioTest_V2):
|
||||
resp['router_portid_1'] = router_portid_1
|
||||
resp['router_portid_2'] = router_portid_2
|
||||
|
||||
# Create a VM on each of the network and assign it a floating IP.
|
||||
security_group = self.create_security_group()
|
||||
self.create_loginable_secgroup_rule(
|
||||
secgroup_id=security_group['id'])
|
||||
self.create_pingable_secgroup_rule(
|
||||
secgroup_id=security_group['id'])
|
||||
# Create a VM on each network and assign it a floating IP. Port
|
||||
# security is disabled because this test exercises L3 FWaaS rules on
|
||||
# the router interfaces, not security-group enforcement on VM ports.
|
||||
server1, private_key1, server_fixed_ip_1, server_floating_ip_1 = (
|
||||
self._create_test_server(network1, security_group))
|
||||
self._create_test_server(network1))
|
||||
server2, private_key2, server_fixed_ip_2, server_floating_ip_2 = (
|
||||
self._create_test_server(network2, security_group))
|
||||
self._create_test_server(network2))
|
||||
resp['server1'] = server1
|
||||
resp['private_key1'] = private_key1
|
||||
resp['server_fixed_ip_1'] = server_fixed_ip_1
|
||||
@@ -203,8 +162,13 @@ class TestFWaaS_v2(base.FWaaSScenarioTest_V2):
|
||||
fw_allow_ssh_rule = self.create_firewall_rule(action="allow",
|
||||
protocol="tcp",
|
||||
destination_port=22)
|
||||
fw_policy = self.create_firewall_policy(
|
||||
firewall_rules=[fw_allow_icmp_rule['id'], fw_allow_ssh_rule['id']])
|
||||
fw_rules = [fw_allow_icmp_rule['id'], fw_allow_ssh_rule['id']]
|
||||
if CONF.fwaas.driver == 'ovn':
|
||||
# OVN FWaaS uses stateless ACLs, so allow SSH replies explicitly.
|
||||
fw_allow_ssh_reply_rule = self.create_firewall_rule(
|
||||
action="allow", protocol="tcp", source_port=22)
|
||||
fw_rules.append(fw_allow_ssh_reply_rule['id'])
|
||||
fw_policy = self.create_firewall_policy(firewall_rules=fw_rules)
|
||||
fw_group = self.create_firewall_group(
|
||||
ports=[
|
||||
topology['router_portid_1'],
|
||||
@@ -213,7 +177,7 @@ class TestFWaaS_v2(base.FWaaSScenarioTest_V2):
|
||||
egress_firewall_policy_id=fw_policy['id'])
|
||||
self.addCleanup(self.update_firewall_group_and_wait, fw_group['id'],
|
||||
ports=[])
|
||||
self._wait_firewall_group_ready(fw_group['id'])
|
||||
self._wait_firewall_group_active(fw_group['id'])
|
||||
LOG.debug('fw_allow_icmp_rule: %s\nfw_allow_ssh_rule: %s\n'
|
||||
'fw_policy: %s\nfw_group: %s\n',
|
||||
fw_allow_icmp_rule, fw_allow_ssh_rule, fw_policy, fw_group)
|
||||
@@ -366,7 +330,7 @@ class TestFWaaS_v2(base.FWaaSScenarioTest_V2):
|
||||
egress_firewall_policy_id=fw_policy['id'])
|
||||
self.addCleanup(self.update_firewall_group_and_wait, fw_group['id'],
|
||||
ports=[])
|
||||
self._wait_firewall_group_ready(fw_group['id'])
|
||||
self._wait_firewall_group_active(fw_group['id'])
|
||||
LOG.debug('Firewall group with empty policy attached to router port')
|
||||
|
||||
self.check_ssh_connectivity(
|
||||
@@ -395,3 +359,126 @@ class TestFWaaS_v2(base.FWaaSScenarioTest_V2):
|
||||
ssh_user=ssh_login,
|
||||
ssh_key=private_key,
|
||||
servers=[topology['server']])
|
||||
|
||||
|
||||
class TestFWaaS_v2_L2(base.FWaaSScenarioTest_V2):
|
||||
"""L2 FWaaS scenario tests for VM port connectivity.
|
||||
|
||||
Firewall groups are attached directly to compute ports. VMs use the
|
||||
project default security group so port security stays enabled and L2
|
||||
FWaaS rules are applied by the OVS driver.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
if CONF.fwaas.driver == 'ovn':
|
||||
raise self.skipException(
|
||||
"FWaaS OVN driver does not support L2 ports.")
|
||||
super().setUp()
|
||||
|
||||
def _create_l2_topology(self):
|
||||
"""Create two VMs on the same network with one floating IP.
|
||||
|
||||
+--------+ +-------------+
|
||||
| VM-1 | | subnet |
|
||||
| (+FIP) +-------------+ network |
|
||||
+--------+ +------+------+
|
||||
|
|
||||
+--------+ |
|
||||
| VM-2 +------------------+
|
||||
| (fixed)|
|
||||
+--------+
|
||||
router (external gateway for FIP on VM-1)
|
||||
"""
|
||||
network, subnet = self._create_network_subnet(
|
||||
prefix='fwaas-l2-',
|
||||
port_security_enabled=True)
|
||||
self._create_router_with_external_gateway(
|
||||
'fwaas-l2-router', subnet_id=subnet['id'])
|
||||
|
||||
pub_network_id = CONF.network.public_network_id
|
||||
server1, keys1, port1 = self._create_server(network)
|
||||
server2, _, port2 = self._create_server(network)
|
||||
server1_floating_ip = self.create_floatingip(
|
||||
external_network_id=pub_network_id, port=port1)
|
||||
server1_fixed_ip = port1['fixed_ips'][0]['ip_address']
|
||||
server2_fixed_ip = port2['fixed_ips'][0]['ip_address']
|
||||
server1_port_id = port1['id']
|
||||
server2_port_id = port2['id']
|
||||
|
||||
return {
|
||||
'network': network,
|
||||
'subnet': subnet,
|
||||
'server1': server1,
|
||||
'server2': server2,
|
||||
'server1_private_key': keys1['private_key'],
|
||||
'server1_floating_ip': server1_floating_ip,
|
||||
'server1_fixed_ip': server1_fixed_ip,
|
||||
'server2_fixed_ip': server2_fixed_ip,
|
||||
'server1_port_id': server1_port_id,
|
||||
'server2_port_id': server2_port_id,
|
||||
}
|
||||
|
||||
@decorators.idempotent_id('c4f8a2b1-6d3e-4a7f-9b0c-1e2d3f4a5b6c')
|
||||
def test_vm_connectivity_with_fwaas_l2(self):
|
||||
"""Verify east-west connectivity between VMs using L2 FWaaS rules.
|
||||
|
||||
VM-1 uses a dedicated firewall group with allow SSH/ICMP so FIP access
|
||||
works while L2 FWaaS rules are enforced. VM-2 is moved to a dedicated
|
||||
L2 firewall group with a deny-ICMP rule until allow-ICMP is added.
|
||||
"""
|
||||
topology = self._create_l2_topology()
|
||||
ssh_login = CONF.validation.image_ssh_user
|
||||
fip_address = topology['server1_floating_ip']['floating_ip_address']
|
||||
|
||||
self._dissociate_ports_from_default_firewall_group(
|
||||
[topology['server1_port_id'], topology['server2_port_id']])
|
||||
|
||||
(_, _, _, fw_ingress_policy,
|
||||
fw_egress_policy) = self._create_fip_access_policies()
|
||||
|
||||
self.create_firewall_group_and_wait(
|
||||
ports=[topology['server1_port_id']],
|
||||
ingress_firewall_policy_id=fw_ingress_policy['id'],
|
||||
egress_firewall_policy_id=fw_egress_policy['id'])
|
||||
|
||||
self.check_connectivity(
|
||||
host=fip_address,
|
||||
ssh_user=ssh_login,
|
||||
ssh_key=topology['server1_private_key'],
|
||||
servers=[topology['server1']])
|
||||
|
||||
ssh_source = ssh.Client(
|
||||
fip_address,
|
||||
ssh_login,
|
||||
pkey=topology['server1_private_key'])
|
||||
|
||||
fw_deny_icmp_rule = self.create_firewall_rule(action="deny",
|
||||
protocol="icmp")
|
||||
fw_policy = self.create_firewall_policy(
|
||||
firewall_rules=[fw_deny_icmp_rule['id']])
|
||||
fw_group = self.create_firewall_group_and_wait(
|
||||
ports=[topology['server2_port_id']],
|
||||
ingress_firewall_policy_id=fw_policy['id'],
|
||||
egress_firewall_policy_id=fw_policy['id'])
|
||||
|
||||
self._check_server_connectivity(
|
||||
topology['server1_floating_ip'],
|
||||
topology['server1_private_key'],
|
||||
address_list=[topology['server2_fixed_ip']],
|
||||
should_connect=False,
|
||||
ssh_source=ssh_source)
|
||||
|
||||
fw_allow_icmp_rule = self.create_firewall_rule(action="allow",
|
||||
protocol="icmp")
|
||||
self.insert_firewall_rule_in_policy_and_wait(
|
||||
firewall_group_id=fw_group['id'],
|
||||
firewall_rule_id=fw_allow_icmp_rule['id'],
|
||||
firewall_policy_id=fw_policy['id'],
|
||||
insert_before=fw_deny_icmp_rule['id'])
|
||||
|
||||
self._check_server_connectivity(
|
||||
topology['server1_floating_ip'],
|
||||
topology['server1_private_key'],
|
||||
address_list=[topology['server2_fixed_ip']],
|
||||
should_connect=True,
|
||||
ssh_source=ssh_source)
|
||||
+11
-1
@@ -1355,7 +1355,7 @@
|
||||
network_api_extensions_fwaas:
|
||||
- fwaas_v2
|
||||
devstack_localrc:
|
||||
NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_fwaas) | join(',') }}"
|
||||
NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common | difference(['security-group']) + network_api_extensions_fwaas) | join(',') }}"
|
||||
devstack_services:
|
||||
# Neutron services
|
||||
q-agt: true
|
||||
@@ -1364,6 +1364,16 @@
|
||||
q-metering: true
|
||||
q-l3: true
|
||||
devstack_local_conf:
|
||||
post-config:
|
||||
/$NEUTRON_CORE_PLUGIN_CONF:
|
||||
securitygroup:
|
||||
# NOTE(slaweq): using noop driver for Security groups is
|
||||
# needed to allow test FWaaS rules and not let them interact
|
||||
# with security groups as we have also enabled
|
||||
# firewall_l2_driver in the environemnt
|
||||
firewall_driver: noop
|
||||
fwaas:
|
||||
firewall_l2_driver: ovs
|
||||
test-config:
|
||||
$TEMPEST_CONFIG:
|
||||
fwaas:
|
||||
|
||||
Reference in new issue
Block a user