Fix linuxbridge agent startup issue with IPv6
The linuxbridge agent tries to move all the IP addresses from the first interface attached to a bridge at startup. This can fail if the interface has an IPv6 address since IPv6 is always disabled on bridge devices. Change ensure_bridge() to not disable IPv6, and instead move all IPs and default route by family - IPv4 and IPv6. Change-Id: Ic236de04c0203633df49967a9a4528fda13c51df Closes-bug: #1662324
This commit is contained in:
parent
67da6a3122
commit
b6d4d382a9
|
@ -210,8 +210,7 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
return self.ensure_bridge(phy_bridge_name)
|
||||
else:
|
||||
bridge_name = self.get_bridge_name(network_id)
|
||||
ips, gateway = self.get_interface_details(interface)
|
||||
if self.ensure_bridge(bridge_name, interface, ips, gateway):
|
||||
if self.ensure_bridge(bridge_name, interface):
|
||||
return interface
|
||||
|
||||
def ensure_vxlan_bridge(self, network_id, segmentation_id):
|
||||
|
@ -223,15 +222,17 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
{segmentation_id: segmentation_id})
|
||||
return
|
||||
bridge_name = self.get_bridge_name(network_id)
|
||||
self.ensure_bridge(bridge_name, interface)
|
||||
self.ensure_bridge(bridge_name, interface, update_interface=False)
|
||||
return interface
|
||||
|
||||
def get_interface_details(self, interface):
|
||||
def get_interface_details(self, interface, ip_version):
|
||||
device = self.ip.device(interface)
|
||||
ips = device.addr.list(scope='global')
|
||||
ips = device.addr.list(scope='global',
|
||||
ip_version=ip_version)
|
||||
|
||||
# Update default gateway if necessary
|
||||
gateway = device.route.get_gateway(scope='global')
|
||||
gateway = device.route.get_gateway(scope='global',
|
||||
ip_version=ip_version)
|
||||
return ips, gateway
|
||||
|
||||
def ensure_flat_bridge(self, network_id, phy_bridge_name,
|
||||
|
@ -241,9 +242,7 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
return self.ensure_bridge(phy_bridge_name)
|
||||
else:
|
||||
bridge_name = self.get_bridge_name(network_id)
|
||||
ips, gateway = self.get_interface_details(physical_interface)
|
||||
if self.ensure_bridge(bridge_name, physical_interface, ips,
|
||||
gateway):
|
||||
if self.ensure_bridge(bridge_name, physical_interface):
|
||||
return physical_interface
|
||||
|
||||
def ensure_local_bridge(self, network_id, phy_bridge_name):
|
||||
|
@ -314,11 +313,9 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
LOG.debug("Done creating vxlan interface %s", interface)
|
||||
return interface
|
||||
|
||||
def update_interface_ip_details(self, destination, source, ips,
|
||||
gateway):
|
||||
if ips or gateway:
|
||||
dst_device = self.ip.device(destination)
|
||||
src_device = self.ip.device(source)
|
||||
def _update_interface_ip_details(self, destination, source, ips, gateway):
|
||||
dst_device = self.ip.device(destination)
|
||||
src_device = self.ip.device(source)
|
||||
|
||||
# Append IP's to bridge if necessary
|
||||
if ips:
|
||||
|
@ -339,6 +336,18 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
for ip in ips:
|
||||
src_device.addr.delete(cidr=ip['cidr'])
|
||||
|
||||
def update_interface_ip_details(self, destination, source):
|
||||
# Returns True if there were IPs or a gateway moved
|
||||
updated = False
|
||||
for ip_version in (constants.IP_VERSION_4, constants.IP_VERSION_6):
|
||||
ips, gateway = self.get_interface_details(source, ip_version)
|
||||
if ips or gateway:
|
||||
self._update_interface_ip_details(destination, source, ips,
|
||||
gateway)
|
||||
updated = True
|
||||
|
||||
return updated
|
||||
|
||||
def _bridge_exists_and_ensure_up(self, bridge_name):
|
||||
"""Check if the bridge exists and make sure it is up."""
|
||||
br = ip_lib.IPDevice(bridge_name)
|
||||
|
@ -350,8 +359,8 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
return False
|
||||
return True
|
||||
|
||||
def ensure_bridge(self, bridge_name, interface=None, ips=None,
|
||||
gateway=None):
|
||||
def ensure_bridge(self, bridge_name, interface=None,
|
||||
update_interface=True):
|
||||
"""Create a bridge unless it already exists."""
|
||||
# _bridge_exists_and_ensure_up instead of device_exists is used here
|
||||
# because there are cases where the bridge exists but it's not UP,
|
||||
|
@ -370,8 +379,6 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
return
|
||||
if bridge_device.disable_stp():
|
||||
return
|
||||
if bridge_device.disable_ipv6():
|
||||
return
|
||||
if bridge_device.link.set_up():
|
||||
return
|
||||
LOG.debug("Done starting bridge %(bridge_name)s for "
|
||||
|
@ -384,7 +391,8 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
return bridge_name
|
||||
|
||||
# Update IP info if necessary
|
||||
self.update_interface_ip_details(bridge_name, interface, ips, gateway)
|
||||
if update_interface:
|
||||
self.update_interface_ip_details(bridge_name, interface)
|
||||
|
||||
# Check if the interface is part of the bridge
|
||||
if not bridge_device.owns_interface(interface):
|
||||
|
@ -517,12 +525,9 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
# If the bridge has an IP, it mean that this IP was moved
|
||||
# from the current interface, which also mean that this
|
||||
# interface was not created by the agent.
|
||||
ips, gateway = self.get_interface_details(bridge_name)
|
||||
if ips:
|
||||
self.update_interface_ip_details(interface,
|
||||
bridge_name,
|
||||
ips, gateway)
|
||||
elif interface not in physical_interfaces:
|
||||
updated = self.update_interface_ip_details(interface,
|
||||
bridge_name)
|
||||
if not updated and interface not in physical_interfaces:
|
||||
self.delete_interface(interface)
|
||||
|
||||
try:
|
||||
|
|
|
@ -260,32 +260,18 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
ip_version=4,
|
||||
dynamic=False)
|
||||
list_fn.return_value = ipdict
|
||||
ret = self.lbm.get_interface_details("eth0")
|
||||
ret = self.lbm.get_interface_details("eth0", 4)
|
||||
|
||||
self.assertTrue(list_fn.called)
|
||||
self.assertTrue(getgw_fn.called)
|
||||
self.assertEqual(ret, (ipdict, gwdict))
|
||||
|
||||
def test_ensure_flat_bridge(self):
|
||||
with mock.patch.object(ip_lib.IpAddrCommand, 'list') as list_fn,\
|
||||
mock.patch.object(ip_lib.IpRouteCommand,
|
||||
'get_gateway') as getgw_fn:
|
||||
gwdict = dict(gateway='1.1.1.1')
|
||||
getgw_fn.return_value = gwdict
|
||||
ipdict = dict(cidr='1.1.1.1/24',
|
||||
broadcast='1.1.1.255',
|
||||
scope='global',
|
||||
ip_version=4,
|
||||
dynamic=False)
|
||||
list_fn.return_value = ipdict
|
||||
with mock.patch.object(self.lbm, 'ensure_bridge') as ens:
|
||||
self.assertEqual(
|
||||
"eth0",
|
||||
self.lbm.ensure_flat_bridge("123", None, "eth0"))
|
||||
self.assertTrue(list_fn.called)
|
||||
self.assertTrue(getgw_fn.called)
|
||||
ens.assert_called_once_with("brq123", "eth0",
|
||||
ipdict, gwdict)
|
||||
with mock.patch.object(self.lbm, 'ensure_bridge') as ens:
|
||||
self.assertEqual(
|
||||
"eth0",
|
||||
self.lbm.ensure_flat_bridge("123", None, "eth0"))
|
||||
ens.assert_called_once_with("brq123", "eth0")
|
||||
|
||||
def test_ensure_flat_bridge_with_existed_brq(self):
|
||||
with mock.patch.object(self.lbm, 'ensure_bridge') as ens:
|
||||
|
@ -298,25 +284,21 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
|
||||
def test_ensure_vlan_bridge(self):
|
||||
with mock.patch.object(self.lbm, 'ensure_vlan') as ens_vl_fn,\
|
||||
mock.patch.object(self.lbm, 'ensure_bridge') as ens,\
|
||||
mock.patch.object(self.lbm,
|
||||
'get_interface_details') as get_int_det_fn:
|
||||
mock.patch.object(self.lbm, 'ensure_bridge') as ens:
|
||||
ens_vl_fn.return_value = "eth0.1"
|
||||
get_int_det_fn.return_value = (None, None)
|
||||
self.assertEqual("eth0.1",
|
||||
self.lbm.ensure_vlan_bridge("123",
|
||||
None,
|
||||
"eth0",
|
||||
"1"))
|
||||
ens.assert_called_with("brq123", "eth0.1", None, None)
|
||||
ens.assert_called_with("brq123", "eth0.1")
|
||||
|
||||
get_int_det_fn.return_value = ("ips", "gateway")
|
||||
self.assertEqual("eth0.1",
|
||||
self.lbm.ensure_vlan_bridge("123",
|
||||
None,
|
||||
"eth0",
|
||||
"1"))
|
||||
ens.assert_called_with("brq123", "eth0.1", "ips", "gateway")
|
||||
ens.assert_called_with("brq123", "eth0.1")
|
||||
|
||||
def test_ensure_vlan_bridge_with_existed_brq(self):
|
||||
with mock.patch.object(self.lbm, 'ensure_vlan') as ens_vl_fn,\
|
||||
|
@ -385,7 +367,7 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
cfg.CONF.set_override('arp_responder', True, 'VXLAN')
|
||||
self.test_ensure_vxlan(expected_proxy=True)
|
||||
|
||||
def test_update_interface_ip_details(self):
|
||||
def test__update_interface_ip_details(self):
|
||||
gwdict = dict(gateway='1.1.1.1',
|
||||
metric=50)
|
||||
ipdict = dict(cidr='1.1.1.1/24',
|
||||
|
@ -395,8 +377,8 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
dynamic=False)
|
||||
with mock.patch.object(ip_lib.IpAddrCommand, 'add') as add_fn,\
|
||||
mock.patch.object(ip_lib.IpAddrCommand, 'delete') as del_fn:
|
||||
self.lbm.update_interface_ip_details("br0", "eth0",
|
||||
[ipdict], None)
|
||||
self.lbm._update_interface_ip_details("br0", "eth0",
|
||||
[ipdict], None)
|
||||
self.assertTrue(add_fn.called)
|
||||
self.assertTrue(del_fn.called)
|
||||
|
||||
|
@ -404,8 +386,8 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
'add_gateway') as addgw_fn,\
|
||||
mock.patch.object(ip_lib.IpRouteCommand,
|
||||
'delete_gateway') as delgw_fn:
|
||||
self.lbm.update_interface_ip_details("br0", "eth0",
|
||||
None, gwdict)
|
||||
self.lbm._update_interface_ip_details("br0", "eth0",
|
||||
None, gwdict)
|
||||
self.assertTrue(addgw_fn.called)
|
||||
self.assertTrue(delgw_fn.called)
|
||||
|
||||
|
@ -441,11 +423,7 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
|
||||
bridge_device.owns_interface.return_value = False
|
||||
self.lbm.ensure_bridge("br0", "eth0")
|
||||
upd_fn.assert_called_with("br0", "eth0", None, None)
|
||||
bridge_device.owns_interface.assert_called_with("eth0")
|
||||
|
||||
self.lbm.ensure_bridge("br0", "eth0", "ips", "gateway")
|
||||
upd_fn.assert_called_with("br0", "eth0", "ips", "gateway")
|
||||
upd_fn.assert_called_with("br0", "eth0")
|
||||
bridge_device.owns_interface.assert_called_with("eth0")
|
||||
|
||||
de_fn.return_value = True
|
||||
|
@ -587,8 +565,6 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
mock.patch.object(bridge_lib.BridgeDevice,
|
||||
"get_interfaces") as getif_fn,\
|
||||
mock.patch.object(self.lbm, "remove_interface"),\
|
||||
mock.patch.object(self.lbm,
|
||||
"get_interface_details") as if_det_fn,\
|
||||
mock.patch.object(self.lbm,
|
||||
"update_interface_ip_details") as updif_fn,\
|
||||
mock.patch.object(self.lbm, "delete_interface") as delif_fn:
|
||||
|
@ -598,10 +574,9 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
|
||||
de_fn.return_value = True
|
||||
getif_fn.return_value = ["eth0", "eth1", "vxlan-1002"]
|
||||
if_det_fn.return_value = ("ips", "gateway")
|
||||
link_cmd.set_down.return_value = False
|
||||
self.lbm.delete_bridge("br0")
|
||||
updif_fn.assert_called_with("eth1", "br0", "ips", "gateway")
|
||||
updif_fn.assert_called_with("eth1", "br0")
|
||||
delif_fn.assert_called_with("vxlan-1002")
|
||||
|
||||
def test_delete_bridge_not_exist(self):
|
||||
|
@ -622,8 +597,6 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
bridge_device = mock.Mock()
|
||||
with mock.patch.object(ip_lib, "device_exists") as de_fn,\
|
||||
mock.patch.object(self.lbm, "remove_interface"),\
|
||||
mock.patch.object(self.lbm,
|
||||
"get_interface_details") as if_det_fn,\
|
||||
mock.patch.object(self.lbm,
|
||||
"update_interface_ip_details") as updif_fn,\
|
||||
mock.patch.object(self.lbm,
|
||||
|
@ -631,11 +604,11 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
mock.patch.object(bridge_lib, "BridgeDevice",
|
||||
return_value=bridge_device):
|
||||
de_fn.return_value = True
|
||||
updif_fn.return_value = True
|
||||
bridge_device.get_interfaces.return_value = ["eth0", "eth1.1"]
|
||||
if_det_fn.return_value = ("ips", "gateway")
|
||||
bridge_device.link.set_down.return_value = False
|
||||
self.lbm.delete_bridge("br0")
|
||||
updif_fn.assert_called_with("eth1.1", "br0", "ips", "gateway")
|
||||
updif_fn.assert_called_with("eth1.1", "br0")
|
||||
self.assertFalse(del_interface.called)
|
||||
|
||||
def test_delete_bridge_no_ip(self):
|
||||
|
@ -645,7 +618,7 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
mock.patch.object(self.lbm,
|
||||
"get_interface_details") as if_det_fn,\
|
||||
mock.patch.object(self.lbm,
|
||||
"update_interface_ip_details") as updif_fn,\
|
||||
"_update_interface_ip_details") as updif_fn,\
|
||||
mock.patch.object(self.lbm,
|
||||
"delete_interface") as del_interface,\
|
||||
mock.patch.object(bridge_lib, "BridgeDevice",
|
||||
|
@ -684,13 +657,13 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
with mock.patch.object(ip_lib, "device_exists") as de_fn,\
|
||||
mock.patch.object(self.lbm, "remove_interface"),\
|
||||
mock.patch.object(self.lbm,
|
||||
"get_interface_details") as if_det_fn,\
|
||||
"update_interface_ip_details") as updif_fn,\
|
||||
mock.patch.object(self.lbm, "delete_interface") as del_int,\
|
||||
mock.patch.object(bridge_lib, "BridgeDevice",
|
||||
return_value=bridge_device):
|
||||
de_fn.return_value = True
|
||||
bridge_device.get_interfaces.return_value = ["eth1.1", "eth1.4000"]
|
||||
if_det_fn.return_value = ([], None)
|
||||
updif_fn.return_value = False
|
||||
bridge_device.link.set_down.return_value = False
|
||||
self.lbm.delete_bridge("br0")
|
||||
del_int.assert_called_once_with("eth1.1")
|
||||
|
|
Loading…
Reference in New Issue