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:
Brian Haley 2017-02-09 10:48:07 -05:00 committed by Brian Haley
parent 67da6a3122
commit b6d4d382a9
2 changed files with 51 additions and 73 deletions

View File

@ -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:

View File

@ -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")