ifcfg: restart child interfaces on updates

If a top level bridge or bond is modified we also want to
restart the child interfaces.
This commit is contained in:
Dan Prince
2014-08-25 17:29:06 -04:00
parent b0ecc03512
commit 82bfaf3f7b
2 changed files with 84 additions and 19 deletions

View File

@@ -50,11 +50,22 @@ class IfcfgNetConfig(os_net_config.NetConfig):
"""Configure network interfaces using the ifcfg format.""" """Configure network interfaces using the ifcfg format."""
def __init__(self): def __init__(self):
self.interfaces = {} self.interface_data = {}
self.routes = {} self.route_data = {}
self.bridges = {} self.bridge_data = {}
self.member_names = {}
logger.info('Ifcfg net config provider created.') logger.info('Ifcfg net config provider created.')
def child_members(self, name):
children = []
try:
for member in self.member_names[name]:
#children.append(member)
children.extend(self.child_members(member))
except KeyError:
children.append(name)
return children
def _add_common(self, base_opt): def _add_common(self, base_opt):
ovs_extra = [] ovs_extra = []
@@ -83,6 +94,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
data += "OVSBOOTPROTO=dhcp\n" data += "OVSBOOTPROTO=dhcp\n"
if base_opt.members: if base_opt.members:
members = [member.name for member in base_opt.members] members = [member.name for member in base_opt.members]
self.member_names[base_opt.name] = members
data += ("OVSDHCPINTERFACES=\"%s\"\n" % " ".join(members)) data += ("OVSDHCPINTERFACES=\"%s\"\n" % " ".join(members))
if base_opt.primary_interface_name: if base_opt.primary_interface_name:
mac = utils.interface_mac(base_opt.primary_interface_name) mac = utils.interface_mac(base_opt.primary_interface_name)
@@ -98,6 +110,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
data += "OVSBOOTPROTO=dhcp\n" data += "OVSBOOTPROTO=dhcp\n"
if base_opt.members: if base_opt.members:
members = [member.name for member in base_opt.members] members = [member.name for member in base_opt.members]
self.member_names[base_opt.name] = members
data += ("BOND_IFACES=\"%s\"\n" % " ".join(members)) data += ("BOND_IFACES=\"%s\"\n" % " ".join(members))
if base_opt.ovs_options: if base_opt.ovs_options:
data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options
@@ -145,8 +158,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
data += "%s via %s dev %s\n" % (route.ip_netmask, data += "%s via %s dev %s\n" % (route.ip_netmask,
route.next_hop, route.next_hop,
interface_name) interface_name)
self.routes[interface_name] = first_line + data self.route_data[interface_name] = first_line + data
logger.debug('route data: %s' % self.routes[interface_name]) logger.debug('route data: %s' % self.route_data[interface_name])
def add_interface(self, interface): def add_interface(self, interface):
"""Add an Interface object to the net config object. """Add an Interface object to the net config object.
@@ -156,7 +169,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.info('adding interface: %s' % interface.name) logger.info('adding interface: %s' % interface.name)
data = self._add_common(interface) data = self._add_common(interface)
logger.debug('interface data: %s' % data) logger.debug('interface data: %s' % data)
self.interfaces[interface.name] = data self.interface_data[interface.name] = data
if interface.routes: if interface.routes:
self._add_routes(interface.name, interface.routes) self._add_routes(interface.name, interface.routes)
@@ -168,7 +181,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.info('adding vlan: %s' % vlan.name) logger.info('adding vlan: %s' % vlan.name)
data = self._add_common(vlan) data = self._add_common(vlan)
logger.debug('vlan data: %s' % data) logger.debug('vlan data: %s' % data)
self.interfaces[vlan.name] = data self.interface_data[vlan.name] = data
if vlan.routes: if vlan.routes:
self._add_routes(vlan.name, vlan.routes) self._add_routes(vlan.name, vlan.routes)
@@ -180,7 +193,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.info('adding bridge: %s' % bridge.name) logger.info('adding bridge: %s' % bridge.name)
data = self._add_common(bridge) data = self._add_common(bridge)
logger.debug('bridge data: %s' % data) logger.debug('bridge data: %s' % data)
self.bridges[bridge.name] = data self.bridge_data[bridge.name] = data
if bridge.routes: if bridge.routes:
self._add_routes(bridge.name, bridge.routes) self._add_routes(bridge.name, bridge.routes)
@@ -192,7 +205,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.info('adding bond: %s' % bond.name) logger.info('adding bond: %s' % bond.name)
data = self._add_common(bond) data = self._add_common(bond)
logger.debug('bond data: %s' % data) logger.debug('bond data: %s' % data)
self.interfaces[bond.name] = data self.interface_data[bond.name] = data
if bond.routes: if bond.routes:
self._add_routes(bond.name, bond.routes) self._add_routes(bond.name, bond.routes)
@@ -201,7 +214,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
:param noop: A boolean which indicates whether this is a no-op. :param noop: A boolean which indicates whether this is a no-op.
:param cleanup: A boolean which indicates whether any undefined :param cleanup: A boolean which indicates whether any undefined
(existing but not present in the object model) interfaces (existing but not present in the object model) interface
should be disabled and deleted. should be disabled and deleted.
:returns: a dict of the format: filename/data which contains info :returns: a dict of the format: filename/data which contains info
for each file that was changed (or would be changed if in --noop for each file that was changed (or would be changed if in --noop
@@ -213,8 +226,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
update_files = {} update_files = {}
all_file_names = [] all_file_names = []
for interface_name, iface_data in self.interfaces.iteritems(): for interface_name, iface_data in self.interface_data.iteritems():
route_data = self.routes.get(interface_name, '') route_data = self.route_data.get(interface_name, '')
interface_path = ifcfg_config_path(interface_name) interface_path = ifcfg_config_path(interface_name)
route_path = route_config_path(interface_name) route_path = route_config_path(interface_name)
all_file_names.append(interface_path) all_file_names.append(interface_path)
@@ -222,13 +235,14 @@ class IfcfgNetConfig(os_net_config.NetConfig):
if (utils.diff(interface_path, iface_data) or if (utils.diff(interface_path, iface_data) or
utils.diff(route_path, route_data)): utils.diff(route_path, route_data)):
restart_interfaces.append(interface_name) restart_interfaces.append(interface_name)
restart_interfaces.extend(self.child_members(interface_name))
update_files[interface_path] = iface_data update_files[interface_path] = iface_data
update_files[route_path] = route_data update_files[route_path] = route_data
logger.info('No changes required for interface: %s' % logger.info('No changes required for interface: %s' %
interface_name) interface_name)
for bridge_name, bridge_data in self.bridges.iteritems(): for bridge_name, bridge_data in self.bridge_data.iteritems():
route_data = self.routes.get(bridge_name, '') route_data = self.route_data.get(bridge_name, '')
bridge_path = bridge_config_path(bridge_name) bridge_path = bridge_config_path(bridge_name)
bridge_route_path = route_config_path(bridge_name) bridge_route_path = route_config_path(bridge_name)
all_file_names.append(bridge_path) all_file_names.append(bridge_path)
@@ -236,6 +250,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
if (utils.diff(bridge_path, bridge_data) or if (utils.diff(bridge_path, bridge_data) or
utils.diff(bridge_route_path, route_data)): utils.diff(bridge_route_path, route_data)):
restart_bridges.append(bridge_name) restart_bridges.append(bridge_name)
restart_interfaces.extend(self.child_members(bridge_name))
update_files[bridge_path] = bridge_data update_files[bridge_path] = bridge_data
update_files[bridge_route_path] = route_data update_files[bridge_route_path] = route_data
logger.info('No changes required for bridge: %s' % bridge_name) logger.info('No changes required for bridge: %s' % bridge_name)

View File

@@ -118,10 +118,10 @@ class TestIfcfgNetConfig(base.TestCase):
super(TestIfcfgNetConfig, self).tearDown() super(TestIfcfgNetConfig, self).tearDown()
def get_interface_config(self, name='em1'): def get_interface_config(self, name='em1'):
return self.provider.interfaces[name] return self.provider.interface_data[name]
def get_route_config(self, name='em1'): def get_route_config(self, name='em1'):
return self.provider.routes.get(name, '') return self.provider.route_data.get(name, '')
def test_add_base_interface(self): def test_add_base_interface(self):
interface = objects.Interface('em1') interface = objects.Interface('em1')
@@ -165,7 +165,7 @@ class TestIfcfgNetConfig(base.TestCase):
self.provider.add_bridge(bridge) self.provider.add_bridge(bridge)
self.assertEqual(_OVS_INTERFACE, self.get_interface_config()) self.assertEqual(_OVS_INTERFACE, self.get_interface_config())
self.assertEqual(_OVS_BRIDGE_DHCP, self.assertEqual(_OVS_BRIDGE_DHCP,
self.provider.bridges['br-ctlplane']) self.provider.bridge_data['br-ctlplane'])
def test_network_ovs_bridge_with_dhcp_primary_interface(self): def test_network_ovs_bridge_with_dhcp_primary_interface(self):
def test_interface_mac(name): def test_interface_mac(name):
@@ -179,7 +179,7 @@ class TestIfcfgNetConfig(base.TestCase):
self.provider.add_bridge(bridge) self.provider.add_bridge(bridge)
self.assertEqual(_OVS_INTERFACE, self.get_interface_config()) self.assertEqual(_OVS_INTERFACE, self.get_interface_config())
self.assertEqual(_OVS_BRIDGE_DHCP_PRIMARY_INTERFACE, self.assertEqual(_OVS_BRIDGE_DHCP_PRIMARY_INTERFACE,
self.provider.bridges['br-ctlplane']) self.provider.bridge_data['br-ctlplane'])
def test_network_ovs_bridge_with_dhcp_primary_interface_with_extra(self): def test_network_ovs_bridge_with_dhcp_primary_interface_with_extra(self):
def test_interface_mac(name): def test_interface_mac(name):
@@ -195,7 +195,7 @@ class TestIfcfgNetConfig(base.TestCase):
self.provider.add_bridge(bridge) self.provider.add_bridge(bridge)
self.assertEqual(_OVS_INTERFACE, self.get_interface_config()) self.assertEqual(_OVS_INTERFACE, self.get_interface_config())
self.assertEqual(_OVS_BRIDGE_DHCP_OVS_EXTRA, self.assertEqual(_OVS_BRIDGE_DHCP_OVS_EXTRA,
self.provider.bridges['br-ctlplane']) self.provider.bridge_data['br-ctlplane'])
def test_add_vlan(self): def test_add_vlan(self):
vlan = objects.Vlan('em1', 5) vlan = objects.Vlan('em1', 5)
@@ -244,6 +244,7 @@ class TestIfcfgNetConfigApply(base.TestCase):
self.temp_route_file = tempfile.NamedTemporaryFile() self.temp_route_file = tempfile.NamedTemporaryFile()
self.temp_bridge_file = tempfile.NamedTemporaryFile() self.temp_bridge_file = tempfile.NamedTemporaryFile()
self.temp_cleanup_file = tempfile.NamedTemporaryFile(delete=False) self.temp_cleanup_file = tempfile.NamedTemporaryFile(delete=False)
self.ifup_interface_names = []
def test_ifcfg_path(name): def test_ifcfg_path(name):
return self.temp_ifcfg_file.name return self.temp_ifcfg_file.name
@@ -262,6 +263,8 @@ class TestIfcfgNetConfigApply(base.TestCase):
self.stubs.Set(impl_ifcfg, 'cleanup_pattern', test_cleanup_pattern) self.stubs.Set(impl_ifcfg, 'cleanup_pattern', test_cleanup_pattern)
def test_execute(*args, **kwargs): def test_execute(*args, **kwargs):
if args[0] == '/sbin/ifup':
self.ifup_interface_names.append(args[1])
pass pass
self.stubs.Set(processutils, 'execute', test_execute) self.stubs.Set(processutils, 'execute', test_execute)
@@ -305,6 +308,53 @@ class TestIfcfgNetConfigApply(base.TestCase):
route_data = utils.get_file_data(self.temp_route_file.name) route_data = utils.get_file_data(self.temp_route_file.name)
self.assertEqual("", route_data) self.assertEqual("", route_data)
def test_restart_children_on_change(self):
# setup and apply a bridge
interface = objects.Interface('em1')
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
members=[interface])
self.provider.add_interface(interface)
self.provider.add_bridge(bridge)
self.provider.apply()
# changing the bridge should restart the interface too
self.ifup_interface_names = []
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=False,
members=[interface])
self.provider.add_interface(interface)
self.provider.add_bridge(bridge)
self.provider.apply()
self.assertIn('em1', self.ifup_interface_names)
self.assertIn('br-ctlplane', self.ifup_interface_names)
# setup and apply a bond on a bridge
self.ifup_interface_names = []
interface1 = objects.Interface('em1')
interface2 = objects.Interface('em2')
bond = objects.OvsBond('bond0',
members=[interface1, interface2])
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
members=[bond])
self.provider.add_interface(interface1)
self.provider.add_interface(interface2)
self.provider.add_bond(bond)
self.provider.add_bridge(bridge)
self.provider.apply()
# changing the bridge should restart everything
self.ifup_interface_names = []
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=False,
members=[bond])
self.provider.add_interface(interface1)
self.provider.add_interface(interface2)
self.provider.add_bond(bond)
self.provider.add_bridge(bridge)
self.provider.apply()
self.assertIn('br-ctlplane', self.ifup_interface_names)
self.assertIn('bond0', self.ifup_interface_names)
self.assertIn('em1', self.ifup_interface_names)
self.assertIn('em2', self.ifup_interface_names)
def test_vlan_apply(self): def test_vlan_apply(self):
vlan = objects.Vlan('em1', 5) vlan = objects.Vlan('em1', 5)
self.provider.add_vlan(vlan) self.provider.add_vlan(vlan)