lb-agent: handle exception when bridge slave already removed
An exception can happen when a network is deleted because the lb-agent tries to removes the dhcp tap from the bridge at about the same time as the dhcp-agent is deleting the tap. The unhandled exception means the bridge does not get deleted and a log error. Closes-Bug: #1611612 Change-Id: Ia9a6b5fc49e239769e850e9486454e81e3a4b96f
This commit is contained in:
parent
87517709f2
commit
72720f9aa3
|
@ -559,19 +559,30 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
||||||
def remove_interface(self, bridge_name, interface_name):
|
def remove_interface(self, bridge_name, interface_name):
|
||||||
bridge_device = bridge_lib.BridgeDevice(bridge_name)
|
bridge_device = bridge_lib.BridgeDevice(bridge_name)
|
||||||
if bridge_device.exists():
|
if bridge_device.exists():
|
||||||
if not bridge_lib.is_bridged_interface(interface_name):
|
if not bridge_device.owns_interface(interface_name):
|
||||||
return True
|
return True
|
||||||
LOG.debug("Removing device %(interface_name)s from bridge "
|
LOG.debug("Removing device %(interface_name)s from bridge "
|
||||||
"%(bridge_name)s",
|
"%(bridge_name)s",
|
||||||
{'interface_name': interface_name,
|
{'interface_name': interface_name,
|
||||||
'bridge_name': bridge_name})
|
'bridge_name': bridge_name})
|
||||||
if bridge_device.delif(interface_name):
|
try:
|
||||||
return False
|
bridge_device.delif(interface_name)
|
||||||
LOG.debug("Done removing device %(interface_name)s from bridge "
|
LOG.debug("Done removing device %(interface_name)s from "
|
||||||
"%(bridge_name)s",
|
"bridge %(bridge_name)s",
|
||||||
{'interface_name': interface_name,
|
{'interface_name': interface_name,
|
||||||
'bridge_name': bridge_name})
|
'bridge_name': bridge_name})
|
||||||
return True
|
return True
|
||||||
|
except RuntimeError:
|
||||||
|
with excutils.save_and_reraise_exception() as ctxt:
|
||||||
|
if not bridge_device.owns_interface(interface_name):
|
||||||
|
# the exception was likely a side effect of the tap
|
||||||
|
# being deleted by some other agent during handling
|
||||||
|
ctxt.reraise = False
|
||||||
|
LOG.debug("Cannot remove %(interface_name)s from "
|
||||||
|
"%(bridge_name)s. It is not on the bridge.",
|
||||||
|
{'interface_name': interface_name,
|
||||||
|
'bridge_name': bridge_name})
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
LOG.debug("Cannot remove device %(interface_name)s bridge "
|
LOG.debug("Cannot remove device %(interface_name)s bridge "
|
||||||
"%(bridge_name)s does not exist",
|
"%(bridge_name)s does not exist",
|
||||||
|
|
|
@ -706,25 +706,36 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
||||||
|
|
||||||
def test_remove_interface(self):
|
def test_remove_interface(self):
|
||||||
with mock.patch.object(ip_lib.IPDevice, "exists") as de_fn,\
|
with mock.patch.object(ip_lib.IPDevice, "exists") as de_fn,\
|
||||||
mock.patch.object(bridge_lib,
|
mock.patch.object(bridge_lib.BridgeDevice,
|
||||||
'is_bridged_interface') as isdev_fn,\
|
'owns_interface') as owns_fn,\
|
||||||
mock.patch.object(bridge_lib.BridgeDevice,
|
mock.patch.object(bridge_lib.BridgeDevice,
|
||||||
"delif") as delif_fn:
|
"delif") as delif_fn:
|
||||||
de_fn.return_value = False
|
de_fn.return_value = False
|
||||||
self.assertFalse(self.lbm.remove_interface("br0", "eth0"))
|
self.assertFalse(self.lbm.remove_interface("br0", "eth0"))
|
||||||
self.assertFalse(isdev_fn.called)
|
self.assertFalse(owns_fn.called)
|
||||||
|
|
||||||
de_fn.return_value = True
|
de_fn.return_value = True
|
||||||
isdev_fn.return_value = False
|
owns_fn.return_value = False
|
||||||
self.assertTrue(self.lbm.remove_interface("br0", "eth0"))
|
self.assertTrue(self.lbm.remove_interface("br0", "eth0"))
|
||||||
|
|
||||||
isdev_fn.return_value = True
|
|
||||||
delif_fn.return_value = True
|
|
||||||
self.assertFalse(self.lbm.remove_interface("br0", "eth0"))
|
|
||||||
|
|
||||||
delif_fn.return_value = False
|
delif_fn.return_value = False
|
||||||
self.assertTrue(self.lbm.remove_interface("br0", "eth0"))
|
self.assertTrue(self.lbm.remove_interface("br0", "eth0"))
|
||||||
|
|
||||||
|
def test_remove_interface_not_on_bridge(self):
|
||||||
|
bridge_device = mock.Mock()
|
||||||
|
with mock.patch.object(bridge_lib, "BridgeDevice",
|
||||||
|
return_value=bridge_device):
|
||||||
|
bridge_device.exists.return_value = True
|
||||||
|
bridge_device.delif.side_effect = RuntimeError
|
||||||
|
|
||||||
|
bridge_device.owns_interface.side_effect = [True, False]
|
||||||
|
self.lbm.remove_interface("br0", 'tap0')
|
||||||
|
self.assertEqual(2, bridge_device.owns_interface.call_count)
|
||||||
|
|
||||||
|
bridge_device.owns_interface.side_effect = [True, True]
|
||||||
|
self.assertRaises(RuntimeError,
|
||||||
|
self.lbm.remove_interface, "br0", 'tap0')
|
||||||
|
|
||||||
def test_delete_interface(self):
|
def test_delete_interface(self):
|
||||||
with mock.patch.object(ip_lib.IPDevice, "exists") as de_fn,\
|
with mock.patch.object(ip_lib.IPDevice, "exists") as de_fn,\
|
||||||
mock.patch.object(ip_lib.IpLinkCommand, "set_down") as down_fn,\
|
mock.patch.object(ip_lib.IpLinkCommand, "set_down") as down_fn,\
|
||||||
|
|
Loading…
Reference in New Issue