Remove port resource change after unplug

Ports are removed (unplug) from the br-int, but
the ovs-agent resource_cache still have them.
If during this removal period, the port attributes
changed/updated. Even worse if the update events
were not received by the agent, as well as the
port plug to the br-int again. The ovs-agent will
use the obsolete resource informaction of the port.
Something may not be processed well then.

So, resource cache should be delete after port removed
from br-int. This patch addresses this issue.

Closes-Bug: #2089661

Change-Id: Ia628ee196382b0672ce3424d7cb86f29a15a6d4d
This commit is contained in:
LIU Yulong 2024-11-26 17:37:56 +08:00 committed by liuyulong
parent 559672f777
commit 6f06d8fa66
3 changed files with 47 additions and 0 deletions

View File

@ -202,6 +202,17 @@ class RemoteResourceCache:
resource_id=resource.id,
states=(existing, resource)))
def record_resource_remove(self, rtype, resource_id):
filters = {'id': (resource_id, )}
for i in self._get_query_ids(rtype, filters):
try:
self._satisfied_server_queries.remove(i)
except KeyError:
continue
LOG.debug("Remove resource cache for resource %s: %s",
rtype, resource_id)
self._type_cache(rtype).pop(resource_id, None)
def record_resource_delete(self, context, rtype, resource_id):
# deletions are final, record them so we never
# accept new data for the same ID.

View File

@ -773,6 +773,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
self._deferred_delete_direct_flows(self.deleted_ports)
rcache_rpc = self.plugin_rpc.remote_resource_cache
while self.deleted_ports:
port_id = self.deleted_ports.pop()
port = self.int_br.get_vif_port_by_id(port_id)
@ -788,6 +789,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
# removing the port from the bridge at the same time
self.port_dead(port, log_errors=False)
self.port_unbound(port_id)
rcache_rpc.record_resource_remove(resources.PORT, port_id)
# Flush firewall rules after ports are put on dead VLAN to be
# more secure
@ -2142,9 +2144,13 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
failed_devices = set(devices_down.get('failed_devices_down'))
LOG.debug("Port removal failed for %s", failed_devices)
self._deferred_delete_direct_flows(devices)
rcache_rpc = self.plugin_rpc.remote_resource_cache
for device in devices:
self.ext_manager.delete_port(self.context, {'port_id': device})
self.port_unbound(device)
if device:
rcache_rpc.record_resource_remove(resources.PORT, device)
return failed_devices
def treat_ancillary_devices_removed(self, devices):

View File

@ -1095,6 +1095,23 @@ class TestOvsNeutronAgent:
self.assertEqual({dev_mock},
failed_devices.get('removed'))
def test_treat_devices_removed_rcache_removed(self):
device_id = 'dev_id'
rcache = self.agent.plugin_rpc.remote_resource_cache
rcache._cache_by_type_and_id['Port'][device_id] = 1
with mock.patch.object(self.agent.plugin_rpc,
'update_device_list',
return_value={'devices_up': [],
'devices_down': [],
'failed_devices_up': [],
'failed_devices_down': []}):
with mock.patch.object(self.agent.int_br,
'get_vif_port_by_id',
return_value=None):
self.agent.treat_devices_removed([device_id])
self.assertNotIn(device_id,
rcache._cache_by_type_and_id['Port'])
def test_treat_devices_removed_ext_delete_port(self):
port_id = 'fake-id'
@ -1561,6 +1578,19 @@ class TestOvsNeutronAgent:
log_errors=False)
int_br.drop_port.assert_called_once_with(in_port=vif.ofport)
def test_port_delete_rcache_removed(self):
vif = FakeVif()
port_id = 'id'
rcache = self.agent.plugin_rpc.remote_resource_cache
rcache._cache_by_type_and_id['Port'][port_id] = 1
with mock.patch.object(self.agent, 'int_br') as int_br:
int_br.get_vif_by_port_id.return_value = vif.port_name
int_br.get_vif_port_by_id.return_value = vif
self.agent.port_delete("unused_context",
port_id=port_id)
self.agent.process_deleted_ports(port_info={})
self.assertNotIn(port_id, rcache._cache_by_type_and_id['Port'])
def test_port_delete_removed_port(self):
with mock.patch.object(self.agent, 'int_br') as int_br:
self.agent.port_delete("unused_context",