Skip to content

Commit c40f637

Browse files
committed
[OVN] Warn about invalid OVN and FIP PF config during start of Neutron
In case when port_forwarding service plugin is enabled and vlan or flat network (provider network types) is configured as one of the tenant_network_types in the ML2 config there is an issue with centralized and distributed traffic. FIP port forwarding in ovn backend are implemented as OVN Load balancers thus are always centralized but if "enable_distributed_floating_ip" is set to True, FIPs are distributed. And in such case it won't work as expected as either it tries to send FIP PF's traffic as distributed when "reside-on-redirect-chassis" for LRP is set to "false" or tries to centralized everything (even FIP which should be distributed) when "reside-on-redirect-chassis" is set to "true". It's not really easy to avoid that issue from the code so this patch adds warning in the upgrade checks and also log warning about it during start of the neutron server process to at least warn cloud admin that such potential issue may happen in the cloud. Conflicts: neutron/cmd/upgrade_checks/checks.py neutron/common/ovn/utils.py neutron/tests/unit/cmd/upgrade_checks/test_checks.py Related-Bug: #2028846 Change-Id: I398f3f676c59dc794cf03320fa45efc7b22fc003 (cherry picked from commit ce53fb5) (cherry picked from commit 1c6d3d448d419d6176647dc0e50760c19d3fefcb)
1 parent f26a625 commit c40f637

File tree

8 files changed

+190
-12
lines changed

8 files changed

+190
-12
lines changed

neutron/cmd/upgrade_checks/checks.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727

2828
from neutron._i18n import _
2929
from neutron.cmd.upgrade_checks import base
30+
from neutron.common.ovn import exceptions as ovn_exc
31+
from neutron.common.ovn import utils as ovn_utils
32+
from neutron.conf.plugins.ml2 import config as ml2_conf
33+
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
3034
from neutron.conf import service as conf_service
3135
from neutron.db.extra_dhcp_opt import models as extra_dhcp_opt_models
3236
from neutron.db.models import agent as agent_model
@@ -179,6 +183,8 @@ def get_checks(self):
179183
self.floatingip_inherit_qos_from_network),
180184
(_('Port extra DHCP options check'),
181185
self.extra_dhcp_options_check),
186+
(_('Floating IP Port forwarding and OVN L3 plugin configuration'),
187+
self.ovn_port_forwarding_configuration_check),
182188
]
183189

184190
@staticmethod
@@ -515,3 +521,28 @@ def extra_dhcp_options_check(checker):
515521
upgradecheck.Code.SUCCESS,
516522
_('There are no extra_dhcp_opts with the newline character '
517523
'in the option name or option value.'))
524+
525+
@staticmethod
526+
def ovn_port_forwarding_configuration_check(checker):
527+
ovn_l3_plugin_names = [
528+
'ovn-router',
529+
'neutron.services.ovn_l3.plugin.OVNL3RouterPlugin']
530+
if not any(plugin in ovn_l3_plugin_names
531+
for plugin in cfg.CONF.service_plugins):
532+
return upgradecheck.Result(
533+
upgradecheck.Code.SUCCESS, _('No OVN L3 plugin enabled.'))
534+
535+
ml2_conf.register_ml2_plugin_opts()
536+
ovn_conf.register_opts()
537+
try:
538+
ovn_utils.validate_port_forwarding_configuration()
539+
return upgradecheck.Result(
540+
upgradecheck.Code.SUCCESS,
541+
_('OVN L3 plugin and Port Forwarding configuration are fine.'))
542+
except ovn_exc.InvalidPortForwardingConfiguration:
543+
return upgradecheck.Result(
544+
upgradecheck.Code.WARNING,
545+
_('Neutron configuration is invalid. Port forwardings '
546+
'can not be used with ML2/OVN backend, distributed '
547+
'floating IPs and provider network type(s) used as '
548+
'tenant networks.'))

neutron/common/ovn/exceptions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,10 @@ class HashRingIsEmpty(n_exc.NeutronException):
3737
'%(node_count)d nodes were found offline. This should never '
3838
'happen in a normal situation, please check the status '
3939
'of your cluster')
40+
41+
42+
class InvalidPortForwardingConfiguration(n_exc.NeutronException):
43+
message = _('Neutron configuration is invalid. Port forwardings '
44+
'can not be used with ML2/OVN backend, distributed '
45+
'floating IPs and provider network type(s) used as '
46+
'tenant networks.')

neutron/common/ovn/utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,3 +1211,20 @@ def get_requested_chassis(requested_chassis):
12111211
# becomes the norm and older versions of OVN are no longer supported
12121212
def is_additional_chassis_supported(idl):
12131213
return idl.is_col_present('Port_Binding', 'additional_chassis')
1214+
1215+
1216+
def validate_port_forwarding_configuration():
1217+
if not ovn_conf.is_ovn_distributed_floating_ip():
1218+
return
1219+
1220+
pf_plugin_names = [
1221+
'port_forwarding',
1222+
'neutron.services.portforwarding.pf_plugin.PortForwardingPlugin']
1223+
if not any(plugin in pf_plugin_names
1224+
for plugin in cfg.CONF.service_plugins):
1225+
return
1226+
1227+
provider_network_types = ['vlan', 'flat']
1228+
if any(net_type in provider_network_types
1229+
for net_type in cfg.CONF.ml2.tenant_network_types):
1230+
raise ovn_exc.InvalidPortForwardingConfiguration()

neutron/services/portforwarding/drivers/ovn/driver.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,18 @@
1010
# License for the specific language governing permissions and limitations
1111
# under the License.
1212

13-
from oslo_log import log
14-
15-
from ovsdbapp.backend.ovs_idl import idlutils
16-
from ovsdbapp import constants as ovsdbapp_const
17-
1813
from neutron_lib.callbacks import events
1914
from neutron_lib.callbacks import registry
2015
from neutron_lib.callbacks import resources
2116
from neutron_lib import constants as const
2217
from neutron_lib.plugins import constants as plugin_constants
2318
from neutron_lib.plugins import directory
19+
from oslo_log import log
20+
from ovsdbapp.backend.ovs_idl import idlutils
21+
from ovsdbapp import constants as ovsdbapp_const
2422

2523
from neutron.common.ovn import constants as ovn_const
24+
from neutron.common.ovn import exceptions as ovn_exc
2625
from neutron.common.ovn import utils as ovn_utils
2726
from neutron.db import ovn_revision_numbers_db as db_rev
2827
from neutron import manager
@@ -192,10 +191,27 @@ def port_forwarding_deleted(self, ovn_txn, nb_ovn, pf_obj):
192191
class OVNPortForwarding(object):
193192

194193
def __init__(self, l3_plugin):
194+
self._validate_configuration()
195195
self._l3_plugin = l3_plugin
196196
self._pf_plugin_property = None
197197
self._handler = OVNPortForwardingHandler()
198198

199+
def _validate_configuration(self):
200+
"""This method checks if Neutron config is compatible with OVN and PFs.
201+
202+
It stops process in case when provider network types (vlan/flat)
203+
are enabled as tenant networks AND distributed floating IPs are enabled
204+
as this configuration is not working fine with FIP PFs in ML2/OVN case.
205+
"""
206+
try:
207+
ovn_utils.validate_port_forwarding_configuration()
208+
except ovn_exc.InvalidPortForwardingConfiguration:
209+
LOG.warning("Neutron configuration is invalid for port "
210+
"forwardings and ML2/OVN backend. "
211+
"It is not valid to use together provider network "
212+
"types (vlan/flat) as tenant networks, distributed "
213+
"floating IPs and port forwardings.")
214+
199215
@property
200216
def _pf_plugin(self):
201217
if self._pf_plugin_property is None:

neutron/tests/unit/cmd/upgrade_checks/test_checks.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
from oslo_upgradecheck.upgradecheck import Code
1919

2020
from neutron.cmd.upgrade_checks import checks
21+
from neutron.common.ovn import exceptions as ovn_exc
22+
from neutron.common.ovn import utils as ovn_utils
2123
from neutron.tests import base
2224

2325

@@ -278,3 +280,25 @@ def test_extra_dhcp_options_check_bad_value(self):
278280
opt_value='bar\nbar')]
279281
result = checks.CoreChecks.extra_dhcp_options_check(mock.ANY)
280282
self.assertEqual(Code.WARNING, result.code)
283+
284+
def test_ovn_port_forwarding_configuration_check_ovn_l3_success(self):
285+
cfg.CONF.set_override("service_plugins", 'ovn-router')
286+
with mock.patch.object(
287+
ovn_utils,
288+
'validate_port_forwarding_configuration') as validate_mock:
289+
result = checks.CoreChecks.ovn_port_forwarding_configuration_check(
290+
mock.ANY)
291+
self.assertEqual(Code.SUCCESS, result.code)
292+
validate_mock.assert_called_once_with()
293+
294+
def test_ovn_port_forwarding_configuration_check_ovn_l3_failure(self):
295+
cfg.CONF.set_override("service_plugins", 'ovn-router')
296+
with mock.patch.object(
297+
ovn_utils,
298+
'validate_port_forwarding_configuration',
299+
side_effect=ovn_exc.InvalidPortForwardingConfiguration
300+
) as validate_mock:
301+
result = checks.CoreChecks.ovn_port_forwarding_configuration_check(
302+
mock.ANY)
303+
self.assertEqual(Code.WARNING, result.code)
304+
validate_mock.assert_called_once_with()

neutron/tests/unit/common/ovn/test_utils.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import testtools
3030

3131
from neutron.common.ovn import constants
32+
from neutron.common.ovn import exceptions as ovn_exc
3233
from neutron.common.ovn import utils
3334
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
3435
from neutron.tests import base
@@ -1172,3 +1173,44 @@ def test_vnic_remote_managed_port_context(self):
11721173
self.fake_smartnic_hostname,
11731174
utils.determine_bind_host(self.mock_sb_idl, {},
11741175
port_context=context))
1176+
1177+
1178+
class ValidatePortForwardingConfigurationTestCase(base.BaseTestCase):
1179+
1180+
def setUp(self):
1181+
super().setUp()
1182+
ovn_conf.register_opts()
1183+
1184+
def test_validation_when_distributed_fip_disabled(self):
1185+
cfg.CONF.set_override(
1186+
'enable_distributed_floating_ip', False, group='ovn')
1187+
cfg.CONF.set_override('service_plugins', 'some_plugin,port_forwarding')
1188+
cfg.CONF.set_override('tenant_network_types', 'geneve,vlan',
1189+
group='ml2')
1190+
utils.validate_port_forwarding_configuration()
1191+
1192+
def test_validation_when_no_pf_plugin_enabled(self):
1193+
cfg.CONF.set_override(
1194+
'enable_distributed_floating_ip', True, group='ovn')
1195+
cfg.CONF.set_override('service_plugins', 'some_plugin')
1196+
cfg.CONF.set_override('tenant_network_types', 'geneve,vlan',
1197+
group='ml2')
1198+
utils.validate_port_forwarding_configuration()
1199+
1200+
def test_validation_when_no_provider_net_configured(self):
1201+
cfg.CONF.set_override(
1202+
'enable_distributed_floating_ip', True, group='ovn')
1203+
cfg.CONF.set_override('service_plugins', 'some_plugin,port_forwarding')
1204+
cfg.CONF.set_override('tenant_network_types', 'geneve,vxlan',
1205+
group='ml2')
1206+
utils.validate_port_forwarding_configuration()
1207+
1208+
def test_validation_when_pf_and_provider_net_enabled(self):
1209+
cfg.CONF.set_override(
1210+
'enable_distributed_floating_ip', True, group='ovn')
1211+
cfg.CONF.set_override('service_plugins', 'some_plugin,port_forwarding')
1212+
cfg.CONF.set_override('tenant_network_types', 'geneve,vlan',
1213+
group='ml2')
1214+
self.assertRaises(
1215+
ovn_exc.InvalidPortForwardingConfiguration,
1216+
utils.validate_port_forwarding_configuration)

neutron/tests/unit/services/portforwarding/drivers/ovn/test_driver.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,25 @@
1414

1515
from unittest import mock
1616

17+
from neutron_lib.callbacks import events
18+
from neutron_lib.callbacks import registry
19+
from neutron_lib.callbacks import resources
20+
from neutron_lib import constants as const
21+
from neutron_lib.plugins import constants as plugin_constants
22+
from oslo_utils import uuidutils
23+
from ovsdbapp import constants as ovsdbapp_const
24+
1725
from neutron.common.ovn import constants as ovn_const
26+
from neutron.common.ovn import exceptions as ovn_exc
27+
from neutron.common.ovn import utils as ovn_utils
28+
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
1829
from neutron.objects import port_forwarding as port_forwarding_obj
1930
from neutron.services.portforwarding.constants import PORT_FORWARDING
2031
from neutron.services.portforwarding.constants import PORT_FORWARDING_PLUGIN
2132
from neutron.services.portforwarding.drivers.ovn import driver \
2233
as port_forwarding
2334
from neutron.tests import base
2435
from neutron.tests.unit import fake_resources
25-
from neutron_lib.callbacks import events
26-
from neutron_lib.callbacks import registry
27-
from neutron_lib.callbacks import resources
28-
from neutron_lib import constants as const
29-
from neutron_lib.plugins import constants as plugin_constants
30-
from oslo_utils import uuidutils
31-
from ovsdbapp import constants as ovsdbapp_const
3236

3337

3438
class TestOVNPortForwardingBase(base.BaseTestCase):
@@ -450,6 +454,7 @@ def test_port_forwarding_deleted(self, m_info):
450454
class TestOVNPortForwarding(TestOVNPortForwardingBase):
451455
def setUp(self):
452456
super(TestOVNPortForwarding, self).setUp()
457+
ovn_conf.register_opts()
453458
self.pf_plugin = mock.Mock()
454459
self.handler = mock.Mock()
455460
get_mock_pf_plugin = lambda alias: self.pf_plugin if (
@@ -475,6 +480,25 @@ def test_init(self):
475480
self.assertEqual(self._ovn_pf._handler, self.handler)
476481
self.assertEqual(self._ovn_pf._pf_plugin, self.pf_plugin)
477482

483+
def test__validate_configuration_ok(self):
484+
with mock.patch.object(
485+
port_forwarding.LOG, "warning") as mock_warning, \
486+
mock.patch.object(ovn_utils,
487+
"validate_port_forwarding_configuration"):
488+
489+
self._ovn_pf._validate_configuration()
490+
mock_warning.assert_not_called()
491+
492+
def test__validate_configuration_wrong(self):
493+
with mock.patch.object(
494+
port_forwarding.LOG, "warning") as mock_warning, \
495+
mock.patch.object(
496+
ovn_utils,
497+
"validate_port_forwarding_configuration",
498+
side_effect=ovn_exc.InvalidPortForwardingConfiguration):
499+
self._ovn_pf._validate_configuration()
500+
mock_warning.assert_called_once_with(mock.ANY)
501+
478502
def test_register(self):
479503
with mock.patch.object(registry, 'subscribe') as mock_subscribe:
480504
self._ovn_pf.register(mock.ANY, mock.ANY, mock.Mock())
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
other:
3+
- |
4+
When the following configuration is enabled at the same time:
5+
6+
* OVN L3 service plugin (``ovn-router``)
7+
* Port forwarding service plugin (``port_forwarding``)
8+
* "vlan" or "flat" network types configured in the ML2 configuration
9+
variable ``tenant_network_types``
10+
* The OVN floating IP traffic is distributed
11+
(``enable_distributed_floating_ip`` = ``True``)
12+
13+
the Neutron server will report a warning during plugin initialization
14+
because this is an invalid configuration matrix. Floating IPs need to
15+
always be centralized in such a case.
16+
For more details see `bug report
17+
<https://bugs.launchpad.net/neutron/+bug/2028846>`_.

0 commit comments

Comments
 (0)