Merge "Avoid vlan device leaking"

This commit is contained in:
Zuul 2023-06-29 09:23:59 +00:00 committed by Gerrit Code Review
commit c930d52563
10 changed files with 107 additions and 7 deletions

View File

@ -154,7 +154,8 @@ class NBOVNBGPDriver(driver_api.AgentDriverBase):
self._ensure_port_exposed(port)
# remove extra wiring leftovers
wire_utils.cleanup_wiring(self.ovn_bridge_mappings,
wire_utils.cleanup_wiring(self.nb_idl,
self.ovn_bridge_mappings,
self.ovs_flows,
self._exposed_ips,
self.ovn_routing_tables,

View File

@ -260,6 +260,9 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
self.ovn_routing_tables_routes,
extra_routes)
wire_utils.delete_vlan_devices_leftovers(self.sb_idl,
self.ovn_bridge_mappings)
def _ensure_cr_lrp_associated_ports_exposed(self, cr_lrp_port,
exposed_ips, ovn_ip_rules):
ips, patch_port_row = self.sb_idl.get_cr_lrp_nat_addresses_info(

View File

@ -181,6 +181,15 @@ class OvsdbNbOvnIdl(nb_impl_idl.OvnNbApiIdlImpl, Backend):
super(OvsdbNbOvnIdl, self).__init__(connection)
self.idl._session.reconnect.set_probe_interval(60000)
def get_network_vlan_tags(self):
tags = []
cmd = self.db_find_rows('Logical_Switch_Port', ('type', '=',
constants.OVN_LOCALNET_VIF_PORT_TYPE))
for row in cmd.execute(check_error=True):
if row.tag:
tags.append(row.tag[0])
return tags
def get_network_vlan_tag_by_network_name(self, network_name):
tags = []
cmd = self.db_find_rows('Logical_Switch_Port', ('type', '=',
@ -340,6 +349,15 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend):
return row.options.get('network_name'), row.tag
return None, None
def get_network_vlan_tags(self):
tags = []
cmd = self.db_find_rows('Port_Binding', ('type', '=',
constants.OVN_LOCALNET_VIF_PORT_TYPE))
for row in cmd.execute(check_error=True):
if row.tag:
tags.append(row.tag[0])
return tags
def get_network_vlan_tag_by_network_name(self, network_name):
tags = []
cmd = self.db_find_rows('Port_Binding', ('type', '=',

View File

@ -65,17 +65,17 @@ def _ensure_base_wiring_config_underlay(idl, bridge_mappings, routing_tables):
return ovn_bridge_mappings, flows_info
def cleanup_wiring(bridge_mappings, ovs_flows, exposed_ips, routing_tables,
routing_tables_routes):
def cleanup_wiring(idl, bridge_mappings, ovs_flows, exposed_ips,
routing_tables, routing_tables_routes):
if CONF.exposing_method == constants.EXPOSE_METHOD_UNDERLAY:
return _cleanup_wiring_underlay(bridge_mappings, ovs_flows,
return _cleanup_wiring_underlay(idl, bridge_mappings, ovs_flows,
exposed_ips, routing_tables,
routing_tables_routes)
elif CONF.exposing_method == constants.EXPOSE_METHOD_OVN:
raise NotImplementedError()
def _cleanup_wiring_underlay(bridge_mappings, ovs_flows, exposed_ips,
def _cleanup_wiring_underlay(idl, bridge_mappings, ovs_flows, exposed_ips,
routing_tables, routing_tables_routes):
current_ips = linux_net.get_exposed_ips(CONF.bgp_nic)
expected_ips = [ip for ip_dict in exposed_ips.values()
@ -109,6 +109,19 @@ def _cleanup_wiring_underlay(bridge_mappings, ovs_flows, exposed_ips,
linux_net.delete_bridge_ip_routes(routing_tables, routing_tables_routes,
extra_routes)
# delete leaked vlan devices from previous vlan provider networks
delete_vlan_devices_leftovers(idl, bridge_mappings)
def delete_vlan_devices_leftovers(idl, bridge_mappings):
vlan_tags = idl.get_network_vlan_tags()
ovs_devices = set(bridge_mappings.values())
for ovs_device in ovs_devices:
vlans = linux_net.get_bridge_vlans(ovs_device)
for vlan in vlans:
if vlan and vlan not in vlan_tags:
linux_net.delete_vlan_device_for_network(ovs_device, vlan)
def wire_provider_port(routing_tables_routes, ovs_flows, port_ips,
bridge_device, bridge_vlan, localnet, routing_table,

View File

@ -439,6 +439,24 @@ def get_link_device(device_name):
return device
@ovn_bgp_agent.privileged.default.entrypoint
def get_bridge_vlans(device_name):
index = _get_link_id(device_name, raise_exception=False)
if not index:
LOG.debug("OVS Bridge %s deleted, no need to get information about "
"associated vlan devices", device_name)
vlan_devices = get_link_devices(link=index)
vlans = []
for vlan_device in vlan_devices:
ifla_linkinfo = get_attr(vlan_device, 'IFLA_LINKINFO')
if ifla_linkinfo:
ifla_data = get_attr(ifla_linkinfo, 'IFLA_INFO_DATA')
if ifla_data:
vlans.append(get_attr(ifla_data, 'IFLA_VLAN_ID'))
return vlans
@tenacity.retry(
retry=tenacity.retry_if_exception_type(
netlink_exceptions.NetlinkDumpInterrupted),

View File

@ -356,6 +356,16 @@ class IpLinkTestCase(_LinuxNetTestCase):
constants.LINK_UP)
test_utils.wait_until_true(fn, timeout=5)
def test_get_bridge_vlan_devices(self):
vlan_id = random.randint(2, 4094)
linux_net.create_interface(self.dev_name, 'dummy')
linux_net.create_interface(self.dev_name2, 'vlan',
physical_interface=self.dev_name,
vlan_id=vlan_id)
vlan_devices = linux_net.get_bridge_vlans(self.dev_name)
self.assertEqual(vlan_devices[0], vlan_id)
class IpAddressTestCase(_LinuxNetTestCase):

View File

@ -108,6 +108,8 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_ensure_ovn_dev.assert_called_once_with(
CONF.bgp_nic, CONF.bgp_vrf)
@mock.patch.object(linux_net, 'delete_vlan_device_for_network')
@mock.patch.object(linux_net, 'get_bridge_vlans')
@mock.patch.object(linux_net, 'get_extra_routing_table_for_bridge')
@mock.patch.object(linux_net, 'delete_bridge_ip_routes')
@mock.patch.object(linux_net, 'delete_ip_rules')
@ -125,7 +127,8 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_ensure_arp, mock_nic_address, mock_get_patch_ports,
mock_ensure_mac, mock_remove_flows, mock_exposed_ips,
mock_get_ip_rules, mock_del_exposed_ips, mock_del_ip_rules,
mock_del_ip_routes, mock_get_extra_route):
mock_del_ip_routes, mock_get_extra_route,
mock_get_bridge_vlans, mock_delete_vlan_dev):
self.mock_ovs_idl.get_ovn_bridge_mappings.return_value = [
'net0:bridge0', 'net1:bridge1']
self.nb_idl.get_network_vlan_tag_by_network_name.side_effect = (
@ -149,6 +152,9 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_nic_address.return_value = self.mac
mock_get_patch_ports.return_value = [1, 2]
self.nb_idl.get_network_vlan_tags.return_value = [10, 11]
mock_get_bridge_vlans.side_effect = [[10, 12], [11]]
self.nb_bgp_driver.sync()
expected_calls = [mock.call({}, 'bridge0', CONF.bgp_vrf_table_id),
@ -177,6 +183,9 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_del_ip_rules.assert_called_once_with(fake_ip_rules)
mock_del_ip_routes.assert_called_once()
bridge = set(self.nb_bgp_driver.ovn_bridge_mappings.values()).pop()
mock_delete_vlan_dev.assert_called_once_with(bridge, 12)
def test__ensure_port_exposed_fip(self):
port0 = fakes.create_object({
'name': 'port-0',

View File

@ -102,6 +102,7 @@ class TestOVNBGPDriver(test_base.TestCase):
mock_ensure_ovn_dev.assert_called_once_with(
CONF.bgp_nic, CONF.bgp_vrf)
@mock.patch.object(wire_utils, 'delete_vlan_devices_leftovers')
@mock.patch.object(linux_net, 'delete_bridge_ip_routes')
@mock.patch.object(linux_net, 'delete_ip_rules')
@mock.patch.object(linux_net, 'delete_exposed_ips')
@ -119,7 +120,7 @@ class TestOVNBGPDriver(test_base.TestCase):
mock_ensure_vlan_network, mock_nic_address, mock_exposed_ips,
mock_get_ip_rules, mock_get_patch_ports, mock_ensure_mac,
mock_remove_flows, mock_del_exposed_ips, mock_del_ip_rules,
mock_del_ip_routes):
mock_del_ip_routes, mock_vlan_leftovers):
self.mock_ovs_idl.get_ovn_bridge_mappings.return_value = [
'net0:bridge0', 'net1:bridge1']
self.sb_idl.get_network_vlan_tag_by_network_name.side_effect = (
@ -183,6 +184,8 @@ class TestOVNBGPDriver(test_base.TestCase):
{'bridge0': ['fake-route'], 'bridge1': ['fake-route']})
mock_get_ip_rules.assert_called_once_with(mock.ANY)
mock_vlan_leftovers.assert_called_once_with(
self.sb_idl, self.bgp_driver.ovn_bridge_mappings)
@mock.patch.object(linux_net, 'get_ip_version')
def test__ensure_cr_lrp_associated_ports_exposed(self, mock_ip_version):

View File

@ -39,6 +39,19 @@ class TestOvsdbNbOvnIdl(test_base.TestCase):
self.nb_idl.db_find_rows = mock.Mock()
self.nb_idl.lookup = mock.Mock()
def test_get_network_vlan_tags(self):
tag = [123]
lsp = fakes.create_object({'name': 'port-0',
'tag': tag})
self.nb_idl.db_find_rows.return_value.execute.return_value = [
lsp]
ret = self.nb_idl.get_network_vlan_tags()
self.assertEqual(tag, ret)
self.nb_idl.db_find_rows.assert_called_once_with(
'Logical_Switch_Port',
('type', '=', constants.OVN_LOCALNET_VIF_PORT_TYPE))
def test_get_network_vlan_tag_by_network_name(self):
network_name = 'net0'
tag = [123]
@ -322,6 +335,14 @@ class TestOvsdbSbOvnIdl(test_base.TestCase):
def test_get_network_name_and_tag_not_in_bridge_mappings(self):
self._test_get_network_name_and_tag(network_in_bridge_map=False)
def test_get_netweork_vlan_tags(self):
tag = [1001]
row = fakes.create_object({'tag': tag})
self.sb_idl.db_find_rows.return_value.execute.return_value = [row, ]
ret = self.sb_idl.get_network_vlan_tags()
self.assertEqual(tag, ret)
def _test_get_network_vlan_tag_by_network_name(self, match=True):
network = 'public' if match else 'spongebob'
tag = [1001]

View File

@ -339,6 +339,10 @@ def delete_vlan_device_for_network(bridge, vlan_tag):
delete_device(vlan_device_name)
def get_bridge_vlans(bridge):
return ovn_bgp_agent.privileged.linux_net.get_bridge_vlans(bridge)
def enable_proxy_ndp(device):
flag = "net.ipv6.conf.{}.proxy_ndp".format(device)
ovn_bgp_agent.privileged.linux_net.set_kernel_flag(flag, 1)