[OVN] Sync the LRP Gateway_Chassis with the network HCG
If a network with external ports is connected to a router (as an internal network), Neutron will create a single ``HA_Chassis_Group`` register for this network, synced with the gateway ``Logical_Router_Port`` ``Gateway_Chassis`` registers. That will schedule the external ports in the same chassis as the gateway port. This patch attends any ``Gateway_Chassis`` change in order to sync the attached networks ``HA_Chassis_Group`` registers, mimicking what is defined the gateway port ``Gateway_Chassis`` list. NOTE: This patch closes the list of patches that implement the functionality defined in LP#2125553. Closes-Bug: #2125553 Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com> Change-Id: Id75fcf72715f9b61aaa0487cdea192e8f9634fd1
This commit is contained in:
committed by
Rodolfo Alonso
parent
e94e7447aa
commit
5e626f8bda
@@ -89,6 +89,15 @@ class ChassisEvent(row_event.RowEvent):
|
|||||||
'HA_Chassis_Group').execute(check_error=True):
|
'HA_Chassis_Group').execute(check_error=True):
|
||||||
if not hcg.name.startswith(ovn_const.OVN_NAME_PREFIX):
|
if not hcg.name.startswith(ovn_const.OVN_NAME_PREFIX):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
net_id = hcg.external_ids.get(ovn_const.OVN_NETWORK_ID_EXT_ID_KEY)
|
||||||
|
router_id = hcg.external_ids.get(
|
||||||
|
ovn_const.OVN_ROUTER_ID_EXT_ID_KEY)
|
||||||
|
if net_id and router_id:
|
||||||
|
# This HA_Chassis_Group is linked to a router, it will be
|
||||||
|
# updated matching the router Gateway_Chassis registers.
|
||||||
|
continue
|
||||||
|
|
||||||
# The filter() is to get rid of the empty string in
|
# The filter() is to get rid of the empty string in
|
||||||
# the list that is returned because of split()
|
# the list that is returned because of split()
|
||||||
azs = {az for az in
|
azs = {az for az in
|
||||||
|
|||||||
@@ -75,3 +75,32 @@ class LogicalRouterPortEvent(row_event.RowEvent):
|
|||||||
else: # LRP gateway port.
|
else: # LRP gateway port.
|
||||||
self.l3_plugin._ovn_client.update_router_ha_chassis_group(
|
self.l3_plugin._ovn_client.update_router_ha_chassis_group(
|
||||||
self.admin_context, router_id)
|
self.admin_context, router_id)
|
||||||
|
|
||||||
|
|
||||||
|
class LogicalRouterPortGatewayChassisEvent(row_event.RowEvent):
|
||||||
|
"""Logical_Router_Port Gateway_Chassis change event.
|
||||||
|
|
||||||
|
When the Gateway_Chassis list of a Logical_Router_Port changes, it is
|
||||||
|
needed to update the linked HA_Chassis_Group registers.
|
||||||
|
"""
|
||||||
|
def __init__(self, driver):
|
||||||
|
self.driver = driver
|
||||||
|
self.l3_plugin = directory.get_plugin(constants.L3)
|
||||||
|
self.admin_context = neutron_context.get_admin_context()
|
||||||
|
table = 'Logical_Router_Port'
|
||||||
|
events = (self.ROW_UPDATE, )
|
||||||
|
super().__init__(events, table, None)
|
||||||
|
|
||||||
|
def match_fn(self, event, row, old):
|
||||||
|
if hasattr(old, 'gateway_chassis'):
|
||||||
|
# NOTE: when a Gateway_Chassis register is deleted, is no longer
|
||||||
|
# present in the old.gateway_chassis list.
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run(self, event, row, old=None):
|
||||||
|
lr_name = row.external_ids.get(ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY)
|
||||||
|
router_id = utils.get_neutron_name(lr_name)
|
||||||
|
self.l3_plugin._ovn_client.update_router_ha_chassis_group(
|
||||||
|
self.admin_context, router_id)
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
|||||||
# Register needed events.
|
# Register needed events.
|
||||||
self._nb_ovn.idl.notify_handler.watch_events([
|
self._nb_ovn.idl.notify_handler.watch_events([
|
||||||
ovsdb_monitor.LogicalRouterPortEvent(self),
|
ovsdb_monitor.LogicalRouterPortEvent(self),
|
||||||
|
ovsdb_monitor.LogicalRouterPortGatewayChassisEvent(self),
|
||||||
])
|
])
|
||||||
|
|
||||||
def _add_neutron_router_interface(self, context, router_id,
|
def _add_neutron_router_interface(self, context, router_id,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from neutron_lib.api.definitions import external_net
|
|||||||
from neutron_lib.plugins import constants as plugin_constants
|
from neutron_lib.plugins import constants as plugin_constants
|
||||||
from neutron_lib.plugins import directory
|
from neutron_lib.plugins import directory
|
||||||
|
|
||||||
|
from neutron.common.ovn import utils as ovn_utils
|
||||||
from neutron.common import utils as n_utils
|
from neutron.common import utils as n_utils
|
||||||
from neutron.tests.functional import base
|
from neutron.tests.functional import base
|
||||||
from neutron.tests.unit.api import test_extensions
|
from neutron.tests.unit.api import test_extensions
|
||||||
@@ -101,3 +102,80 @@ class TestLogicalRouterPortEvent(
|
|||||||
self._router_interface_action(
|
self._router_interface_action(
|
||||||
'remove', self.router_id, self.subnet_id, None)
|
'remove', self.router_id, self.subnet_id, None)
|
||||||
n_utils.wait_until_true(is_called, timeout=10)
|
n_utils.wait_until_true(is_called, timeout=10)
|
||||||
|
|
||||||
|
def test_delete_router(self):
|
||||||
|
# The ``Logical_Router`` deletion triggers the
|
||||||
|
# ``LogicalRouterPortEvent`` event, but nothing is executed/called.
|
||||||
|
def is_called():
|
||||||
|
try:
|
||||||
|
mock_update_router.assert_called_once_with(
|
||||||
|
mock.ANY, self.router_id)
|
||||||
|
return True
|
||||||
|
except AssertionError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
self.l3_plugin._ovn_client,
|
||||||
|
'update_router_ha_chassis_group') as mock_update_router:
|
||||||
|
self._add_external_gateway_to_router(self.router_id,
|
||||||
|
self.net_ext_id)
|
||||||
|
n_utils.wait_until_true(is_called, timeout=10)
|
||||||
|
mock_update_router.reset_mock()
|
||||||
|
req = self.new_delete_request('routers', self.router_id)
|
||||||
|
req.get_response(self.api)
|
||||||
|
self.assertRaises(n_utils.WaitTimeout, n_utils.wait_until_true,
|
||||||
|
is_called, timeout=5)
|
||||||
|
|
||||||
|
|
||||||
|
class TestLogicalRouterPortGatewayChassisEvent(
|
||||||
|
base.TestOVNFunctionalBase,
|
||||||
|
test_l3.L3NatTestCaseMixin):
|
||||||
|
|
||||||
|
def setUp(self, **kwargs):
|
||||||
|
super().setUp(**kwargs)
|
||||||
|
self.chassis = self.add_fake_chassis('ovs-host1')
|
||||||
|
self.l3_plugin = directory.get_plugin(plugin_constants.L3)
|
||||||
|
self.l3_plugin._post_fork_initialize(mock.ANY, mock.ANY, mock.ANY)
|
||||||
|
self.ext_api = test_extensions.setup_extensions_middleware(
|
||||||
|
test_l3.L3TestExtensionManager())
|
||||||
|
kwargs = {'arg_list': (external_net.EXTERNAL,),
|
||||||
|
external_net.EXTERNAL: True}
|
||||||
|
self.net_ext = self._make_network(
|
||||||
|
self.fmt, 'net_ext', True, as_admin=True, **kwargs)
|
||||||
|
self.subnet = self._make_subnet(self.fmt, self.net_ext, '20.0.10.1',
|
||||||
|
'20.0.10.0/24')
|
||||||
|
self.router = self._make_router(self.fmt, self._tenant_id)
|
||||||
|
self.router_id = self.router['router']['id']
|
||||||
|
self.net_ext_id = self.net_ext['network']['id']
|
||||||
|
self.subnet_id = self.subnet['subnet']['id']
|
||||||
|
|
||||||
|
def test_add_and_remove_gateway_chassis(self):
|
||||||
|
def is_called():
|
||||||
|
try:
|
||||||
|
mock_update_router.assert_called_once_with(
|
||||||
|
mock.ANY, self.router_id)
|
||||||
|
return True
|
||||||
|
except AssertionError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
ch_list = []
|
||||||
|
for idx in range(5):
|
||||||
|
ch_list.append(self.add_fake_chassis(f'host-{idx}'))
|
||||||
|
self._add_external_gateway_to_router(self.router_id, self.net_ext_id)
|
||||||
|
lr = self.l3_plugin._nb_ovn.lookup('Logical_Router',
|
||||||
|
ovn_utils.ovn_name(self.router_id))
|
||||||
|
lrp_gw = lr.ports[0]
|
||||||
|
with mock.patch.object(
|
||||||
|
self.l3_plugin._ovn_client,
|
||||||
|
'update_router_ha_chassis_group') as mock_update_router:
|
||||||
|
for ch_name in ch_list:
|
||||||
|
self.l3_plugin._nb_ovn.lrp_set_gateway_chassis(
|
||||||
|
lrp_gw.uuid, ch_name).execute(check_error=True)
|
||||||
|
n_utils.wait_until_true(is_called, timeout=10)
|
||||||
|
mock_update_router.reset_mock()
|
||||||
|
|
||||||
|
for ch_name in ch_list:
|
||||||
|
self.l3_plugin._nb_ovn.lrp_del_gateway_chassis(
|
||||||
|
lrp_gw.uuid, ch_name).execute(check_error=True)
|
||||||
|
n_utils.wait_until_true(is_called, timeout=10)
|
||||||
|
mock_update_router.reset_mock()
|
||||||
|
|||||||
Reference in New Issue
Block a user