From 597bb0d1877f92f9300d7d337bb9920ac82cab9a Mon Sep 17 00:00:00 2001 From: Flavio Fernandes Date: Tue, 14 Jul 2020 12:22:47 -0400 Subject: [PATCH] [ovn]: port forwarding -- IDL changes This is a subset of the changes for implementing the floating IP port forwarding feature in neutron, using OVN as the backend. This changeset covers the additions to northbound api needed for handling load balancer entries created on behalf of port forwarding. Depends-On: https://review.opendev.org/#/c/729354/ Change-Id: I7d0aa51468dbd2c298395c3dccd8f222e87032d8 Partially-implements: ovn/port_forwarding Partial-Bug: #1877447 --- .../ml2/drivers/ovn/mech_driver/ovsdb/api.py | 36 ++++++++++++++ .../drivers/ovn/mech_driver/ovsdb/commands.py | 4 ++ .../ovn/mech_driver/ovsdb/impl_idl_ovn.py | 24 ++++++++++ .../mech_driver/ovsdb/test_impl_idl_ovn.py | 47 ++++++++++++++++++- 4 files changed, 110 insertions(+), 1 deletion(-) diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/api.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/api.py index d19a6804c97..aac70ea76c4 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/api.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/api.py @@ -570,6 +570,42 @@ class API(api.API, metaclass=abc.ABCMeta): :returns: :class:`Command` with no result """ + @abc.abstractmethod + def update_lb_external_ids(self, lb_name, values, if_exists=True): + """Set the external_ids field of a given Load Balancer. + + :param lb_name: The name of the load_balancer + :type lb_name: string + :param values: Values to be set in external_ids + :type values: dict + :param if_exists: Do not fail if lb_name does not exist + :type if_exists: bool + :returns: :class:`Command` with no result + """ + + @abc.abstractmethod + def get_router_floatingip_lbs(self, lrouter_name): + """Get Load Balancers used as port forwarding by a Logical Router. + + :param lrouter_name: The name of the logical router + :type lrouter_name: string + :returns: a list of Load_Balancer rows matched + """ + + @abc.abstractmethod + def get_floatingip_in_nat_or_lb(self, fip_id): + """Get a Floating IP from either NAT or Load Balancer table by its ID + + NAT rows in OVN are mapped for floating IPs, except for port + forwarding. In such cases, Load Balancer table is used . This function + returns a row from NAT, if there is one. Otherwise, it will lookup + for the FIP ID in the LB table and return the first match. + + :param fip_id: The floating IP id + :type fip_id: string + :returns: The NAT rule row or Load_Balancer row or None + """ + class SbAPI(api.API, metaclass=abc.ABCMeta): diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/commands.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/commands.py index 3d133cf5ee8..33599ede7d3 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/commands.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/commands.py @@ -569,6 +569,10 @@ class UpdatePortBindingExtIdsCommand(UpdateObjectExtIdsCommand): field = 'logical_port' +class UpdateLbExternalIds(UpdateObjectExtIdsCommand): + table = 'Load_Balancer' + + class AddDHCPOptionsCommand(command.BaseCommand): def __init__(self, api, subnet_id, port_id=None, may_exist=True, **columns): diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py index 4fed8769fdb..8ca6dc14ea5 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py @@ -33,6 +33,7 @@ from neutron.common.ovn import utils from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as cfg from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import commands as cmd from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovsdb_monitor +from neutron.services.portforwarding import constants as pf_const LOG = log.getLogger(__name__) @@ -617,6 +618,26 @@ class OvsdbNbOvnIdl(nb_impl_idl.OvnNbApiIdlImpl, Backend): return (ls, None) + def get_router_floatingip_lbs(self, lrouter_name): + rc = self.db_find_rows('Load_Balancer', ( + 'external_ids', '=', + {ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY: + pf_const.PORT_FORWARDING_PLUGIN, + ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: lrouter_name})) + return [ovn_obj for ovn_obj in rc.execute(check_error=True) + if ovn_const.OVN_FIP_EXT_ID_KEY in ovn_obj.external_ids] + + def get_floatingip_in_nat_or_lb(self, fip_id): + fip = self.get_floatingip(fip_id) + if fip: + return fip + result = self.db_find('Load_Balancer', ( + 'external_ids', '=', + {ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY: + pf_const.PORT_FORWARDING_PLUGIN, + ovn_const.OVN_FIP_EXT_ID_KEY: fip_id})).execute(check_error=True) + return result[0] if result else None + def get_floatingip(self, fip_id): # TODO(dalvarez): remove this check once the minimum OVS required # version contains the column (when OVS 2.8.2 is released). @@ -717,6 +738,9 @@ class OvsdbNbOvnIdl(nb_impl_idl.OvnNbApiIdlImpl, Backend): return cmd.UnsetLSwitchPortToVirtualTypeCommand( self, lport_name, virtual_parent, if_exists) + def update_lb_external_ids(self, lb_name, values, if_exists=True): + return cmd.UpdateLbExternalIds(self, lb_name, values, if_exists) + class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): def __init__(self, connection): diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py index 19bca463353..4e3c4478248 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py @@ -22,6 +22,7 @@ from neutron.common.ovn import constants as ovn_const from neutron.common.ovn import utils from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import impl_idl_ovn +from neutron.services.portforwarding import constants as pf_const from neutron.tests import base from neutron.tests.unit import fake_resources as fakes @@ -278,7 +279,21 @@ class TestNBImplIdlOvn(TestDBImplIdlOvn): {'name': '$as_ip4_id_5', 'addresses': ['20.0.2.1', '20.0.2.2'], 'external_ids': {ovn_const.OVN_SG_EXT_ID_KEY: 'id_5'}}], - } + 'lbs': [ + {'name': 'lb_1', + 'external_ids': { + ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY: + pf_const.PORT_FORWARDING_PLUGIN, + ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'rtr_name', + ovn_const.OVN_FIP_EXT_ID_KEY: 'fip_id_1'}}, + {'name': 'lb_2', + 'external_ids': { + ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY: + pf_const.PORT_FORWARDING_PLUGIN, + ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'rtr_name', + ovn_const.OVN_FIP_EXT_ID_KEY: 'fip_id_2'}}, + {'name': 'lb_3', 'external_ids': {}}], + } fake_associations = { 'lstolsp': { @@ -328,6 +343,7 @@ class TestNBImplIdlOvn(TestDBImplIdlOvn): self.acl_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self.dhcp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self.address_set_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() + self.lb_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self._tables = {} self._tables['Logical_Switch'] = self.lswitch_table @@ -338,6 +354,7 @@ class TestNBImplIdlOvn(TestDBImplIdlOvn): self._tables['ACL'] = self.acl_table self._tables['DHCP_Options'] = self.dhcp_table self._tables['Address_Set'] = self.address_set_table + self._tables['Load_Balancer'] = self.lb_table with mock.patch.object(impl_idl_ovn, 'get_connection', return_value=mock.Mock()): @@ -399,6 +416,9 @@ class TestNBImplIdlOvn(TestDBImplIdlOvn): # Load address sets fake_address_sets = TestNBImplIdlOvn.fake_set['address_sets'] self._load_ovsdb_fake_rows(self.address_set_table, fake_address_sets) + # Load load balancers + fake_lbs = TestNBImplIdlOvn.fake_set['lbs'] + self._load_ovsdb_fake_rows(self.lb_table, fake_lbs) @mock.patch.object(ovs_idl.Backend, 'autocreate_indices', mock.Mock(), create=True) @@ -782,6 +802,31 @@ class TestNBImplIdlOvn(TestDBImplIdlOvn): port_groups = self.nb_ovn_idl.get_sg_port_groups() self.assertEqual({}, port_groups) + def test_get_router_floatingip_lbs(self): + lrouter_name = 'rtr_name' + # Empty + lbs = self.nb_ovn_idl.get_router_floatingip_lbs(lrouter_name) + self.assertEqual([], lbs) + self._load_nb_db() + lbs = self.nb_ovn_idl.get_router_floatingip_lbs('not_there') + self.assertEqual([], lbs) + lb1_row = self._find_ovsdb_fake_row(self.lb_table, 'name', 'lb_1') + lb2_row = self._find_ovsdb_fake_row(self.lb_table, 'name', 'lb_2') + lbs = self.nb_ovn_idl.get_router_floatingip_lbs(lrouter_name) + self.assertEqual(lbs, [lb1_row, lb2_row]) + + def test_get_floatingip_in_nat_or_lb(self): + fip_id = 'fip_id_2' + # Empty + lb = self.nb_ovn_idl.get_floatingip_in_nat_or_lb(fip_id) + self.assertIsNone(lb) + self._load_nb_db() + lb = self.nb_ovn_idl.get_floatingip_in_nat_or_lb('not_there') + self.assertIsNone(lb) + lb_row = self._find_ovsdb_fake_row(self.lb_table, 'name', 'lb_2') + lb = self.nb_ovn_idl.get_floatingip_in_nat_or_lb(fip_id) + self.assertEqual(lb['_uuid'], lb_row.uuid) + class TestSBImplIdlOvn(TestDBImplIdlOvn):