Skip to content

Commit 7b94421

Browse files
authored
Merge pull request #40 from stackhpc/upstream/yoga-2023-04-10
Synchronise yoga with upstream
2 parents eb4bd1a + c246913 commit 7b94421

File tree

5 files changed

+227
-19
lines changed

5 files changed

+227
-19
lines changed

neutron/common/ovn/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,8 @@
384384
LSP_OPTIONS_MCAST_FLOOD = 'mcast_flood'
385385

386386
LRP_OPTIONS_RESIDE_REDIR_CH = 'reside-on-redirect-chassis'
387+
LRP_OPTIONS_REDIRECT_TYPE = 'redirect-type'
388+
BRIDGE_REDIRECT_TYPE = "bridged"
387389

388390
# Port Binding types
389391
PB_TYPE_VIRTUAL = 'virtual'

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from neutron_lib import context as n_context
2929
from neutron_lib.db import api as db_api
3030
from neutron_lib import exceptions as n_exc
31+
from neutron_lib.exceptions import l3 as l3_exc
3132
from oslo_config import cfg
3233
from oslo_log import log
3334
from oslo_utils import timeutils
@@ -818,6 +819,66 @@ def update_port_qos_with_external_ids_reference(self):
818819
txn.add(cmd)
819820
raise periodics.NeverAgain()
820821

822+
# A static spacing value is used here, but this method will only run
823+
# once per lock due to the use of periodics.NeverAgain().
824+
@periodics.periodic(spacing=600, run_immediately=True)
825+
def check_redirect_type_router_gateway_ports(self):
826+
"""Check OVN router gateway ports
827+
Check for the option "redirect-type=bridged" value for
828+
router gateway ports.
829+
"""
830+
if not self.has_lock:
831+
return
832+
context = n_context.get_admin_context()
833+
cmds = []
834+
gw_ports = self._ovn_client._plugin.get_ports(
835+
context, {'device_owner': [n_const.DEVICE_OWNER_ROUTER_GW]})
836+
for gw_port in gw_ports:
837+
enable_redirect = False
838+
if ovn_conf.is_ovn_distributed_floating_ip():
839+
try:
840+
r_ports = self._ovn_client._get_router_ports(
841+
context, gw_port['device_id'])
842+
except l3_exc.RouterNotFound:
843+
LOG.debug("No Router %s not found", gw_port['device_id'])
844+
continue
845+
else:
846+
network_ids = {port['network_id'] for port in r_ports}
847+
networks = self._ovn_client._plugin.get_networks(
848+
context, filters={'id': network_ids})
849+
# NOTE(ltomasbo): For VLAN type networks connected through
850+
# the gateway port there is a need to set the redirect-type
851+
# option to bridge to ensure traffic is not centralized
852+
# through the controller.
853+
# If there are no VLAN type networks attached we need to
854+
# still make it centralized.
855+
if networks:
856+
enable_redirect = all(
857+
net.get(pnet.NETWORK_TYPE) in [n_const.TYPE_VLAN,
858+
n_const.TYPE_FLAT]
859+
for net in networks)
860+
861+
lrp_name = utils.ovn_lrouter_port_name(gw_port['id'])
862+
lrp = self._nb_idl.get_lrouter_port(lrp_name)
863+
redirect_value = lrp.options.get(
864+
ovn_const.LRP_OPTIONS_REDIRECT_TYPE)
865+
if enable_redirect:
866+
if redirect_value != ovn_const.BRIDGE_REDIRECT_TYPE:
867+
opt = {ovn_const.LRP_OPTIONS_REDIRECT_TYPE:
868+
ovn_const.BRIDGE_REDIRECT_TYPE}
869+
cmds.append(self._nb_idl.db_set(
870+
'Logical_Router_Port', lrp_name, ('options', opt)))
871+
else:
872+
if redirect_value == ovn_const.BRIDGE_REDIRECT_TYPE:
873+
cmds.append(self._nb_idl.db_remove(
874+
'Logical_Router_Port', lrp_name, 'options',
875+
(ovn_const.LRP_OPTIONS_REDIRECT_TYPE)))
876+
if cmds:
877+
with self._nb_idl.transaction(check_error=True) as txn:
878+
for cmd in cmds:
879+
txn.add(cmd)
880+
raise periodics.NeverAgain()
881+
821882
# A static spacing value is used here, but this method will only run
822883
# once per lock due to the use of periodics.NeverAgain().
823884
@periodics.periodic(spacing=600, run_immediately=True)
@@ -840,9 +901,11 @@ def check_vlan_distributed_ports(self):
840901
router_ports = self._ovn_client._plugin.get_ports(
841902
context, {'network_id': vlan_net_ids,
842903
'device_owner': n_const.ROUTER_PORT_OWNERS})
843-
expected_value = ('false' if ovn_conf.is_ovn_distributed_floating_ip()
844-
else 'true')
904+
845905
for rp in router_ports:
906+
expected_value = (
907+
self._ovn_client._get_reside_redir_for_gateway_port(
908+
rp['device_id']))
846909
lrp_name = utils.ovn_lrouter_port_name(rp['id'])
847910
lrp = self._nb_idl.get_lrouter_port(lrp_name)
848911
if lrp.options.get(

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,11 @@ def update_router_routes(self, context, router_id, add, remove,
13201320
nexthop=route['nexthop']))
13211321
self._transaction(commands, txn=txn)
13221322

1323+
def _get_router_gw_ports(self, context, router_id):
1324+
return self._plugin.get_ports(context, filters={
1325+
'device_owner': [const.DEVICE_OWNER_ROUTER_GW],
1326+
'device_id': [router_id]})
1327+
13231328
def _get_router_ports(self, context, router_id, get_gw_port=False):
13241329
# _get_router() will raise a RouterNotFound error if there's no router
13251330
# with the router_id
@@ -1551,6 +1556,31 @@ def _gen_router_port_ext_ids(self, port):
15511556

15521557
return ext_ids
15531558

1559+
def _get_reside_redir_for_gateway_port(self, device_id):
1560+
admin_context = n_context.get_admin_context()
1561+
reside_redir_ch = 'true'
1562+
if ovn_conf.is_ovn_distributed_floating_ip():
1563+
reside_redir_ch = 'false'
1564+
try:
1565+
router_ports = self._get_router_ports(admin_context, device_id)
1566+
except l3_exc.RouterNotFound:
1567+
LOG.debug("No Router %s not found", device_id)
1568+
else:
1569+
network_ids = {port['network_id'] for port in router_ports}
1570+
networks = self._plugin.get_networks(
1571+
admin_context, filters={'id': network_ids})
1572+
1573+
# NOTE(ltomasbo): not all the networks connected to the router
1574+
# are of vlan type, so we won't set the redirect-type=bridged
1575+
# on the router gateway port, therefore we need to centralized
1576+
# the vlan traffic to avoid tunneling
1577+
if networks:
1578+
reside_redir_ch = 'true' if any(
1579+
net.get(pnet.NETWORK_TYPE) not in [const.TYPE_VLAN,
1580+
const.TYPE_FLAT]
1581+
for net in networks) else 'false'
1582+
return reside_redir_ch
1583+
15541584
def _gen_router_port_options(self, port, network=None):
15551585
options = {}
15561586
admin_context = n_context.get_admin_context()
@@ -1565,14 +1595,15 @@ def _gen_router_port_options(self, port, network=None):
15651595
# FIXME(ltomasbo): Once Bugzilla 2162756 is fixed the
15661596
# is_provider_network check should be removed
15671597
if network.get(pnet.NETWORK_TYPE) == const.TYPE_VLAN:
1568-
options[ovn_const.LRP_OPTIONS_RESIDE_REDIR_CH] = (
1569-
'false' if (ovn_conf.is_ovn_distributed_floating_ip() and
1570-
not utils.is_provider_network(network))
1571-
else 'true')
1598+
reside_redir_ch = self._get_reside_redir_for_gateway_port(
1599+
port['device_id'])
1600+
options[ovn_const.LRP_OPTIONS_RESIDE_REDIR_CH] = reside_redir_ch
15721601

15731602
is_gw_port = const.DEVICE_OWNER_ROUTER_GW == port.get(
15741603
'device_owner')
1575-
if is_gw_port and ovn_conf.is_ovn_emit_need_to_frag_enabled():
1604+
1605+
if is_gw_port and (ovn_conf.is_ovn_distributed_floating_ip() or
1606+
ovn_conf.is_ovn_emit_need_to_frag_enabled()):
15761607
try:
15771608
router_ports = self._get_router_ports(admin_context,
15781609
port['device_id'])
@@ -1581,12 +1612,32 @@ def _gen_router_port_options(self, port, network=None):
15811612
LOG.debug("Router %s not found", port['device_id'])
15821613
else:
15831614
network_ids = {port['network_id'] for port in router_ports}
1584-
for net in self._plugin.get_networks(admin_context,
1585-
filters={'id': network_ids}):
1586-
if net['mtu'] > network['mtu']:
1587-
options[ovn_const.OVN_ROUTER_PORT_GW_MTU_OPTION] = str(
1588-
network['mtu'])
1589-
break
1615+
networks = self._plugin.get_networks(
1616+
admin_context, filters={'id': network_ids})
1617+
if ovn_conf.is_ovn_emit_need_to_frag_enabled():
1618+
for net in networks:
1619+
if net['mtu'] > network['mtu']:
1620+
options[
1621+
ovn_const.OVN_ROUTER_PORT_GW_MTU_OPTION] = str(
1622+
network['mtu'])
1623+
break
1624+
if ovn_conf.is_ovn_distributed_floating_ip():
1625+
# NOTE(ltomasbo): For VLAN type networks connected through
1626+
# the gateway port there is a need to set the redirect-type
1627+
# option to bridge to ensure traffic is not centralized
1628+
# through the controller.
1629+
# If there are no VLAN type networks attached we need to
1630+
# still make it centralized.
1631+
enable_redirect = False
1632+
if networks:
1633+
enable_redirect = all(
1634+
net.get(pnet.NETWORK_TYPE) in [const.TYPE_VLAN,
1635+
const.TYPE_FLAT]
1636+
for net in networks)
1637+
if enable_redirect:
1638+
options[ovn_const.LRP_OPTIONS_REDIRECT_TYPE] = (
1639+
ovn_const.BRIDGE_REDIRECT_TYPE)
1640+
15901641
return options
15911642

15921643
def _create_lrouter_port(self, context, router, port, txn=None):
@@ -1667,6 +1718,12 @@ def create_router_port(self, context, router_id, router_interface):
16671718
if utils.is_snat_enabled(router) and cidr:
16681719
self.update_nat_rules(router, networks=[cidr],
16691720
enable_snat=True, txn=txn)
1721+
if ovn_conf.is_ovn_distributed_floating_ip():
1722+
router_gw_ports = self._get_router_gw_ports(context,
1723+
router_id)
1724+
for router_port in router_gw_ports:
1725+
self._update_lrouter_port(context, router_port,
1726+
txn=txn)
16701727

16711728
db_rev.bump_revision(context, port, ovn_const.TYPE_ROUTER_PORTS)
16721729

@@ -1807,6 +1864,11 @@ def delete_router_port(self, context, port_id, router_id=None,
18071864
self.update_nat_rules(
18081865
router, networks=[cidr], enable_snat=False, txn=txn)
18091866

1867+
if ovn_conf.is_ovn_distributed_floating_ip():
1868+
router_gw_ports = self._get_router_gw_ports(context, router_id)
1869+
for router_port in router_gw_ports:
1870+
self._update_lrouter_port(context, router_port, txn=txn)
1871+
18101872
# NOTE(mangelajo): If the port doesn't exist anymore, we
18111873
# delete the router port as the last operation and update the
18121874
# revision database to ensure consistency

neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from unittest import mock
1717

1818
from futurist import periodics
19+
from neutron_lib import constants as n_const
1920
from neutron_lib import context
2021
from neutron_lib.db import api as db_api
2122
from oslo_config import cfg
@@ -622,16 +623,76 @@ def test_update_port_qos_with_external_ids_reference(self):
622623
('external_ids', external_ids))]
623624
nb_idl.db_set.assert_has_calls(expected_calls)
624625

626+
def _test_check_redirect_type_router_gateway_ports(self, networks,
627+
redirect_value):
628+
self.fake_ovn_client._plugin.get_ports.return_value = [{
629+
'device_owner': n_const.DEVICE_OWNER_ROUTER_GW,
630+
'id': 'fake-id',
631+
'device_id': 'fake-device-id'}]
632+
self.fake_ovn_client._get_router_ports.return_value = []
633+
self.fake_ovn_client._plugin.get_networks.return_value = networks
634+
635+
lrp_redirect = fakes.FakeOvsdbRow.create_one_ovsdb_row(
636+
attrs={
637+
'options': {constants.LRP_OPTIONS_REDIRECT_TYPE: "bridged"}})
638+
lrp_no_redirect = fakes.FakeOvsdbRow.create_one_ovsdb_row(
639+
attrs={
640+
'options': {}})
641+
642+
# set the opossite so that the value is changed
643+
if redirect_value:
644+
self.fake_ovn_client._nb_idl.get_lrouter_port.return_value = (
645+
lrp_no_redirect)
646+
else:
647+
self.fake_ovn_client._nb_idl.get_lrouter_port.return_value = (
648+
lrp_redirect)
649+
650+
self.assertRaises(
651+
periodics.NeverAgain,
652+
self.periodic.check_redirect_type_router_gateway_ports)
653+
654+
if redirect_value:
655+
expected_calls = [
656+
mock.call.db_set('Logical_Router_Port',
657+
mock.ANY,
658+
('options', {'redirect-type': 'bridged'}))
659+
]
660+
self.fake_ovn_client._nb_idl.db_set.assert_has_calls(
661+
expected_calls)
662+
else:
663+
expected_calls = [
664+
mock.call.db_remove('Logical_Router_Port', mock.ANY,
665+
'options', 'redirect-type')
666+
]
667+
self.fake_ovn_client._nb_idl.db_remove.assert_has_calls(
668+
expected_calls)
669+
670+
def test_check_redirect_type_router_gateway_ports_enable_redirect(self):
671+
cfg.CONF.set_override('enable_distributed_floating_ip', 'True',
672+
group='ovn')
673+
networks = [{'network_id': 'foo',
674+
'provider:network_type': n_const.TYPE_VLAN}]
675+
self._test_check_redirect_type_router_gateway_ports(networks, True)
676+
677+
def test_check_redirect_type_router_gateway_ports_disable_redirect(self):
678+
cfg.CONF.set_override('enable_distributed_floating_ip', 'True',
679+
group='ovn')
680+
networks = [{'network_id': 'foo',
681+
'provider:network_type': n_const.TYPE_GENEVE}]
682+
self._test_check_redirect_type_router_gateway_ports(networks, False)
683+
625684
def _test_check_vlan_distributed_ports(self, opt_value=None):
626685
fake_net0 = {'id': 'net0'}
627686
fake_net1 = {'id': 'net1'}
628-
fake_port0 = {'id': 'port0'}
629-
fake_port1 = {'id': 'port1'}
687+
fake_port0 = {'id': 'port0', 'device_id': 'device0'}
688+
fake_port1 = {'id': 'port1', 'device_id': 'device1'}
630689

631690
self.fake_ovn_client._plugin.get_networks.return_value = [
632691
fake_net0, fake_net1]
633692
self.fake_ovn_client._plugin.get_ports.return_value = [
634693
fake_port0, fake_port1]
694+
(self.fake_ovn_client._get_reside_redir_for_gateway_port
695+
.return_value) = 'true'
635696

636697
fake_lrp = fakes.FakeOvsdbRow.create_one_ovsdb_row(
637698
attrs={
@@ -645,8 +706,6 @@ def _test_check_vlan_distributed_ports(self, opt_value=None):
645706
self.periodic.check_vlan_distributed_ports)
646707

647708
def test_check_vlan_distributed_ports_expected_value(self):
648-
cfg.CONF.set_override('enable_distributed_floating_ip', 'False',
649-
group='ovn')
650709
self._test_check_vlan_distributed_ports(opt_value='true')
651710

652711
# If the "reside-on-redirect-chassis" option value do match
@@ -655,8 +714,6 @@ def test_check_vlan_distributed_ports_expected_value(self):
655714
self.fake_ovn_client._nb_idl.db_set.called)
656715

657716
def test_check_vlan_distributed_ports_non_expected_value(self):
658-
cfg.CONF.set_override('enable_distributed_floating_ip', 'False',
659-
group='ovn')
660717
self._test_check_vlan_distributed_ports(opt_value='false')
661718

662719
# If the "reside-on-redirect-chassis" option value does not match
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
issues:
3+
- |
4+
The `redirect-type=bridged` option is only used if all the tenant networks
5+
connected to the router are of type VLAN or FLAT. In this case their
6+
traffic will be distributed. However, if there is a mix of VLAN/FLAT and
7+
geneve networks connected to the same router, the redirect-type option is
8+
not set, and therefore the traffic for the VLAN/FLAT networks will also be
9+
centralized but not tunneled.
10+
fixes:
11+
- |
12+
[`bug 2003455 <https://bugs.launchpad.net/neutron/+bug/2003455>`_]
13+
As part of a previous commit
14+
(https://review.opendev.org/c/openstack/neutron/+/875644) the
15+
`redirect-type=bridged` option was set in all the router gateway ports
16+
(cr-lrp ovn ports). However this was breaking the N/S traffic for geneve
17+
tenant networks connected to the provider networks through those routers
18+
with the redirect-type option enabled. To fix this we ensure that the
19+
redirect-type option is only set if all the networks connected to the
20+
router are of VLAN or FLAT type, otherwise we fall back to the default
21+
option. This also means that if there is a mix of VLAN and geneve tenant
22+
networks connected to the same router, the VLAN traffic will be centralized
23+
(but not tunneled). If the traffic for the VLAN/FLAT needs to be
24+
distributed, then it should use a different router.

0 commit comments

Comments
 (0)