summaryrefslogtreecommitdiff
path: root/neutron
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2020-09-05 02:39:59 +0000
committerGerrit Code Review <review@openstack.org>2020-09-05 02:39:59 +0000
commitba0b943638bba8a00c0e74abc8c2141b61b8341d (patch)
tree4dd73cc79652244d2484ff3df91299eb5238e761 /neutron
parent812878b2937eb9d2dbf99602ea7e7aebf6bfb7ee (diff)
parent0deec6621e6485e0a7c219953850fd14b83a7c41 (diff)
downloadneutron-ba0b943638bba8a00c0e74abc8c2141b61b8341d.tar.gz
Merge "[ovn]: port forwarding -- functional tests for maintenance task"
Diffstat (limited to 'neutron')
-rw-r--r--neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py274
1 files changed, 248 insertions, 26 deletions
diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py
index d0b75e9c8b..f6c618126e 100644
--- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py
+++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py
@@ -19,8 +19,10 @@ from oslo_config import cfg
from futurist import periodics
from neutron_lib.api.definitions import external_net as extnet_apidef
+from neutron_lib.api.definitions import floating_ip_port_forwarding as pf_def
from neutron_lib import constants as n_const
from neutron_lib import context as n_context
+from neutron_lib.exceptions import l3 as lib_l3_exc
from neutron.common.ovn import constants as ovn_const
from neutron.common.ovn import utils
@@ -97,34 +99,36 @@ class _TestMaintenanceHelper(base.TestOVNFunctionalBase):
opt_string = ','.join(['{0}:{1}'.format(key, value)
for key, value
in opts.items()])
- if ip_version == 6:
+ if ip_version == n_const.IP_VERSION_6:
ovn_config.cfg.CONF.set_override('ovn_dhcp6_global_options',
opt_string,
group='ovn')
- if ip_version == 4:
+ if ip_version == n_const.IP_VERSION_4:
ovn_config.cfg.CONF.set_override('ovn_dhcp4_global_options',
opt_string,
group='ovn')
def _unset_global_dhcp_opts(self, ip_version):
- if ip_version == 6:
+ if ip_version == n_const.IP_VERSION_6:
ovn_config.cfg.CONF.clear_override('ovn_dhcp6_global_options',
group='ovn')
- if ip_version == 4:
+ if ip_version == n_const.IP_VERSION_4:
ovn_config.cfg.CONF.clear_override('ovn_dhcp4_global_options',
group='ovn')
- def _create_subnet(self, name, net_id, ip_version=4):
+ def _create_subnet(self, name, net_id, ip_version=n_const.IP_VERSION_4,
+ **kwargs):
+ if ip_version == n_const.IP_VERSION_4:
+ cidr = '10.0.0.0/24'
+ else:
+ cidr = '2001:db8::/64'
data = {'subnet': {'name': name,
- 'tenant_id': self._tenant_id,
'network_id': net_id,
'ip_version': ip_version,
+ 'tenant_id': self._tenant_id,
+ 'cidr': cidr,
'enable_dhcp': True}}
- if ip_version == 4:
- data['subnet']['cidr'] = '10.0.0.0/24'
- else:
- data['subnet']['cidr'] = 'eef0::/64'
-
+ data['subnet'].update(kwargs)
req = self.new_create_request('subnets', data, self.fmt)
res = req.get_response(self.api)
return self.deserialize(self.fmt, res)['subnet']
@@ -210,6 +214,21 @@ class _TestMaintenanceHelper(base.TestOVNFunctionalBase):
if row.name == utils.ovn_lrouter_port_name(port_id):
return row
+ def _find_nat_rule(self, router_id, external_ip, logical_ip=None,
+ nat_type='dnat_and_snat'):
+ rules = self.nb_api.get_lrouter_nat_rules(utils.ovn_name(router_id))
+ return next((r for r in rules
+ if r['type'] == nat_type and
+ r['external_ip'] == external_ip and
+ (not logical_ip or r['logical_ip'] == logical_ip)),
+ None)
+
+ def _find_pf_lb(self, router_id, fip_id=None):
+ lbs = self.nb_api.get_router_floatingip_lbs(utils.ovn_name(router_id))
+ return [lb for lb in lbs
+ if (not fip_id or
+ fip_id == lb.external_ids[ovn_const.OVN_FIP_EXT_ID_KEY])]
+
class TestMaintenance(_TestMaintenanceHelper):
@@ -357,7 +376,8 @@ class TestMaintenance(_TestMaintenanceHelper):
self.assertIsNone(ovn_obj.options.get('ntp_server', None))
# Set some global DHCP Options
- self._set_global_dhcp_opts(ip_version=4, opts=options)
+ self._set_global_dhcp_opts(ip_version=n_const.IP_VERSION_4,
+ opts=options)
# Run the maintenance task to add the new options
self.assertRaises(periodics.NeverAgain,
@@ -366,12 +386,13 @@ class TestMaintenance(_TestMaintenanceHelper):
# Assert that the option was added
ovn_obj = self._find_subnet_row_by_id(neutron_sub['id'])
self.assertEqual(
- ovn_obj.options.get('ntp_server', None),
- '1.2.3.4')
+ '1.2.3.4',
+ ovn_obj.options.get('ntp_server', None))
# Change the global option
new_options = {'ntp_server': '4.3.2.1'}
- self._set_global_dhcp_opts(ip_version=4, opts=new_options)
+ self._set_global_dhcp_opts(ip_version=n_const.IP_VERSION_4,
+ opts=new_options)
# Run the maintenance task to update the options
self.assertRaises(periodics.NeverAgain,
@@ -380,12 +401,13 @@ class TestMaintenance(_TestMaintenanceHelper):
# Assert that the option was changed
ovn_obj = self._find_subnet_row_by_id(neutron_sub['id'])
self.assertEqual(
- ovn_obj.options.get('ntp_server', None),
- '4.3.2.1')
+ '4.3.2.1',
+ ovn_obj.options.get('ntp_server', None))
# Change the global option to null
new_options = {'ntp_server': ''}
- self._set_global_dhcp_opts(ip_version=4, opts=new_options)
+ self._set_global_dhcp_opts(ip_version=n_const.IP_VERSION_4,
+ opts=new_options)
# Run the maintenance task to update the options
self.assertRaises(periodics.NeverAgain,
@@ -401,14 +423,16 @@ class TestMaintenance(_TestMaintenanceHelper):
neutron_net = self._create_network('network1')
# Create a subnet without global options
- neutron_sub = self._create_subnet(obj_name, neutron_net['id'], 6)
+ neutron_sub = self._create_subnet(obj_name, neutron_net['id'],
+ n_const.IP_VERSION_6)
# Assert that the option is not set
ovn_obj = self._find_subnet_row_by_id(neutron_sub['id'])
self.assertIsNone(ovn_obj.options.get('ntp_server', None))
# Set some global DHCP Options
- self._set_global_dhcp_opts(ip_version=6, opts=options)
+ self._set_global_dhcp_opts(ip_version=n_const.IP_VERSION_6,
+ opts=options)
# Run the maintenance task to add the new options
self.assertRaises(periodics.NeverAgain,
@@ -417,12 +441,13 @@ class TestMaintenance(_TestMaintenanceHelper):
# Assert that the option was added
ovn_obj = self._find_subnet_row_by_id(neutron_sub['id'])
self.assertEqual(
- ovn_obj.options.get('ntp_server', None),
- '1.2.3.4')
+ '1.2.3.4',
+ ovn_obj.options.get('ntp_server', None))
# Change the global option
new_options = {'ntp_server': '4.3.2.1'}
- self._set_global_dhcp_opts(ip_version=6, opts=new_options)
+ self._set_global_dhcp_opts(ip_version=n_const.IP_VERSION_6,
+ opts=new_options)
# Run the maintenance task to update the options
self.assertRaises(periodics.NeverAgain,
@@ -431,12 +456,13 @@ class TestMaintenance(_TestMaintenanceHelper):
# Assert that the option was changed
ovn_obj = self._find_subnet_row_by_id(neutron_sub['id'])
self.assertEqual(
- ovn_obj.options.get('ntp_server', None),
- '4.3.2.1')
+ '4.3.2.1',
+ ovn_obj.options.get('ntp_server', None))
# Change the global option to null
new_options = {'ntp_server': ''}
- self._set_global_dhcp_opts(ip_version=6, opts=new_options)
+ self._set_global_dhcp_opts(ip_version=n_const.IP_VERSION_6,
+ opts=new_options)
# Run the maintenance task to update the options
self.assertRaises(periodics.NeverAgain,
@@ -821,3 +847,199 @@ class TestMaintenance(_TestMaintenanceHelper):
self.assertEqual('true', ls['other_config'][ovn_const.MCAST_SNOOP])
self.assertEqual(
'true', ls['other_config'][ovn_const.MCAST_FLOOD_UNREGISTERED])
+
+ def test_floating_ip(self):
+ ext_net = self._create_network('ext_networktest', external=True)
+ ext_subnet = self._create_subnet(
+ 'ext_subnettest',
+ ext_net['id'],
+ **{'cidr': '100.0.0.0/24',
+ 'gateway_ip': '100.0.0.254',
+ 'allocation_pools': [
+ {'start': '100.0.0.2', 'end': '100.0.0.253'}],
+ 'enable_dhcp': False})
+ net1 = self._create_network('network1test', external=False)
+ subnet1 = self._create_subnet('subnet1test', net1['id'])
+ external_gateway_info = {
+ 'enable_snat': True,
+ 'network_id': ext_net['id'],
+ 'external_fixed_ips': [
+ {'ip_address': '100.0.0.2', 'subnet_id': ext_subnet['id']}]}
+ router = self._create_router(
+ 'routertest', external_gateway_info=external_gateway_info)
+ self._add_router_interface(router['id'], subnet1['id'])
+
+ p1 = self._create_port('testp1', net1['id'])
+ logical_ip = p1['fixed_ips'][0]['ip_address']
+ fip_info = {'floatingip': {
+ 'description': 'test_fip',
+ 'tenant_id': self._tenant_id,
+ 'floating_network_id': ext_net['id'],
+ 'port_id': p1['id'],
+ 'fixed_ip_address': logical_ip}}
+
+ # > Create
+ with mock.patch.object(self._l3_ovn_client, 'create_floatingip'):
+ fip = self.l3_plugin.create_floatingip(self.context, fip_info)
+
+ floating_ip_address = fip['floating_ip_address']
+ self.assertEqual(router['id'], fip['router_id'])
+ self.assertEqual('testp1', fip['port_details']['name'])
+ self.assertIsNotNone(self.nb_api.get_lswitch_port(fip['port_id']))
+
+ # Assert the dnat_and_snat rule doesn't exist in OVN
+ self.assertIsNone(
+ self._find_nat_rule(router['id'], floating_ip_address, logical_ip))
+
+ # Call the maintenance thread to fix the problem
+ self.maint.check_for_inconsistencies()
+
+ # Assert the rule for the fip is now present
+ self.assertIsNotNone(
+ self._find_nat_rule(router['id'], floating_ip_address, logical_ip))
+
+ # > Update
+ p2 = self._create_port('testp2', net1['id'])
+ logical_ip = p2['fixed_ips'][0]['ip_address']
+ fip_info = {'floatingip': {
+ 'port_id': p2['id'],
+ 'fixed_ip_address': logical_ip}}
+
+ with mock.patch.object(self._l3_ovn_client, 'update_floatingip'):
+ self.l3_plugin.update_floatingip(self.context, fip['id'], fip_info)
+
+ # Assert the dnat_and_snat rule in OVN is still using p1's address
+ stale_nat_rule = self._find_nat_rule(router['id'], floating_ip_address)
+ self.assertEqual(p1['fixed_ips'][0]['ip_address'],
+ stale_nat_rule['logical_ip'])
+
+ # Call the maintenance thread to fix the problem
+ self.maint.check_for_inconsistencies()
+
+ # Assert the rule for the fip is now updated
+ self.assertIsNotNone(
+ self._find_nat_rule(router['id'], floating_ip_address, logical_ip))
+
+ # > Delete
+ with mock.patch.object(self._l3_ovn_client, 'delete_floatingip'):
+ self.l3_plugin.delete_floatingip(self.context, fip['id'])
+
+ self.assertRaises(
+ lib_l3_exc.FloatingIPNotFound,
+ self.l3_plugin.get_floatingip, self.context, fip['id'])
+
+ # Assert the dnat_and_snat rule in OVN is still present
+ self.assertIsNotNone(
+ self._find_nat_rule(router['id'], floating_ip_address, logical_ip))
+
+ # Call the maintenance thread to fix the problem
+ self.maint.check_for_inconsistencies()
+
+ # Assert the rule for the fip is now gone
+ self.assertIsNone(
+ self._find_nat_rule(router['id'], floating_ip_address))
+
+ # Assert the router snat rule is still there
+ snat_rule = self._find_nat_rule(
+ router['id'], '100.0.0.2', nat_type='snat')
+ self.assertEqual(subnet1['cidr'], snat_rule['logical_ip'])
+
+ def test_port_forwarding(self):
+ fip_attrs = lambda args: {
+ pf_def.RESOURCE_NAME: {pf_def.RESOURCE_NAME: args}}
+
+ def _verify_lb(test, protocol, vip_ext_port, vip_int_port):
+ ovn_lbs = self._find_pf_lb(router_id, fip_id)
+ test.assertEqual(1, len(ovn_lbs))
+ test.assertEqual('pf-floatingip-{}-{}'.format(fip_id, protocol),
+ ovn_lbs[0].name)
+ test.assertEqual(
+ {'{}:{}'.format(fip_ip, vip_ext_port):
+ '{}:{}'.format(p1_ip, vip_int_port)},
+ ovn_lbs[0].vips)
+
+ ext_net = self._create_network('ext_networktest', external=True)
+ ext_subnet = self._create_subnet(
+ 'ext_subnettest',
+ ext_net['id'],
+ **{'cidr': '100.0.0.0/24',
+ 'gateway_ip': '100.0.0.254',
+ 'allocation_pools': [
+ {'start': '100.0.0.2', 'end': '100.0.0.253'}],
+ 'enable_dhcp': False})
+ net1 = self._create_network('network1test', external=False)
+ subnet1 = self._create_subnet('subnet1test', net1['id'])
+ external_gateway_info = {
+ 'enable_snat': True,
+ 'network_id': ext_net['id'],
+ 'external_fixed_ips': [
+ {'ip_address': '100.0.0.2', 'subnet_id': ext_subnet['id']}]}
+ router = self._create_router(
+ 'routertest', external_gateway_info=external_gateway_info)
+ router_id = router['id']
+ self._add_router_interface(router['id'], subnet1['id'])
+
+ fip_info = {'floatingip': {
+ 'tenant_id': self._tenant_id,
+ 'floating_network_id': ext_net['id'],
+ 'port_id': None,
+ 'fixed_ip_address': None}}
+ fip = self.l3_plugin.create_floatingip(self.context, fip_info)
+ fip_id = fip['id']
+ fip_ip = fip['floating_ip_address']
+ p1 = self._create_port('testp1', net1['id'])
+ p1_ip = p1['fixed_ips'][0]['ip_address']
+
+ with mock.patch('neutron_lib.callbacks.registry.notify') as m_notify:
+ # > Create
+ fip_pf_args = {
+ pf_def.EXTERNAL_PORT: 2222,
+ pf_def.INTERNAL_PORT: 22,
+ pf_def.INTERNAL_PORT_ID: p1['id'],
+ pf_def.PROTOCOL: 'tcp',
+ pf_def.INTERNAL_IP_ADDRESS: p1_ip}
+ pf_obj = self.pf_plugin.create_floatingip_port_forwarding(
+ self.context, fip_id, **fip_attrs(fip_pf_args))
+ m_notify.assert_called_once()
+
+ # Assert load balancer for port forwarding was not created
+ self.assertFalse(self._find_pf_lb(router_id, fip_id))
+
+ # Call the maintenance thread to fix the problem
+ self.maint.check_for_inconsistencies()
+
+ # Assert load balancer for port forwarding was created
+ _verify_lb(self, 'tcp', 2222, 22)
+
+ # > Update
+ fip_pf_args = {pf_def.EXTERNAL_PORT: 5353,
+ pf_def.INTERNAL_PORT: 53,
+ pf_def.PROTOCOL: 'udp'}
+ m_notify.reset_mock()
+ self.pf_plugin.update_floatingip_port_forwarding(
+ self.context, pf_obj['id'], fip_id, **fip_attrs(fip_pf_args))
+ m_notify.assert_called_once()
+
+ # Assert load balancer for port forwarding is stale
+ _verify_lb(self, 'tcp', 2222, 22)
+
+ # Call the maintenance thread to fix the problem
+ self.maint.check_for_inconsistencies()
+
+ # Assert load balancer for port forwarding was updated
+ _verify_lb(self, 'udp', 5353, 53)
+
+ # > Delete
+ m_notify.reset_mock()
+ self.pf_plugin.delete_floatingip_port_forwarding(
+ self.context, pf_obj['id'], fip_id)
+ m_notify.assert_called_once()
+
+ # Assert load balancer for port forwarding is stale
+ _verify_lb(self, 'udp', 5353, 53)
+
+ # Call the maintenance thread to fix the problem
+ self.maint.check_for_inconsistencies()
+
+ # Assert load balancer for port forwarding is gone
+ self.assertFalse(self._find_pf_lb(router_id, fip_id))