From 3b4a27a3ccb861cf2899ea1fae6c1cc160b38fd7 Mon Sep 17 00:00:00 2001 From: Dan Sneddon Date: Sun, 17 Jan 2016 13:04:40 -0800 Subject: Add ability to set IPv6 routes in os-net-config templates This change adds the ability to write IPv6 routes to the system NIC configuration. In the ifcfg provider, IPv6 routes must be written to network-scripts/route6- rather than route-interface. This patch checks the next hop, and if it is an IPv6 address (assumed because of the presence of ":"), then the route will be written to route6-. This patch includes tests to confirm proper operation. Change-Id: I3563800c774a6082a896c7508f5a92e25418fe63 --- os_net_config/impl_ifcfg.py | 76 +++++++++++++++++++++++++++------- os_net_config/tests/test_impl_ifcfg.py | 36 ++++++++++++++++ 2 files changed, 96 insertions(+), 16 deletions(-) diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py index c4d6d93..27b8579 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -43,6 +43,10 @@ def route_config_path(name): return "/etc/sysconfig/network-scripts/route-%s" % name +def route6_config_path(name): + return "/etc/sysconfig/network-scripts/route6-%s" % name + + def cleanup_pattern(): return "/etc/sysconfig/network-scripts/ifcfg-*" @@ -55,6 +59,7 @@ class IfcfgNetConfig(os_net_config.NetConfig): self.interface_data = {} self.ivsinterface_data = {} self.route_data = {} + self.route6_data = {} self.bridge_data = {} self.linuxbridge_data = {} self.linuxbond_data = {} @@ -221,16 +226,31 @@ class IfcfgNetConfig(os_net_config.NetConfig): logger.info('adding custom route for interface: %s' % interface_name) data = "" first_line = "" + data6 = "" + first_line6 = "" for route in routes: - if route.default: - first_line = "default via %s dev %s\n" % (route.next_hop, - interface_name) + if ":" not in route.next_hop: + # Route is an IPv4 route + if route.default: + first_line = "default via %s dev %s\n" % (route.next_hop, + interface_name) + else: + data += "%s via %s dev %s\n" % (route.ip_netmask, + route.next_hop, + interface_name) else: - data += "%s via %s dev %s\n" % (route.ip_netmask, - route.next_hop, - interface_name) + # Route is an IPv6 route + if route.default: + first_line6 = "default via %s dev %s\n" % (route.next_hop, + interface_name) + else: + data6 += "%s via %s dev %s\n" % (route.ip_netmask, + route.next_hop, + interface_name) self.route_data[interface_name] = first_line + data + self.route6_data[interface_name] = first_line6 + data6 logger.debug('route data: %s' % self.route_data[interface_name]) + logger.debug('ipv6 route data: %s' % self.route6_data[interface_name]) def add_interface(self, interface): """Add an Interface object to the net config object. @@ -379,18 +399,24 @@ class IfcfgNetConfig(os_net_config.NetConfig): for interface_name, iface_data in self.interface_data.iteritems(): route_data = self.route_data.get(interface_name, '') + route6_data = self.route6_data.get(interface_name, '') interface_path = self.root_dir + ifcfg_config_path(interface_name) route_path = self.root_dir + route_config_path(interface_name) + route6_path = self.root_dir + route6_config_path(interface_name) all_file_names.append(interface_path) all_file_names.append(route_path) if "IVS_BRIDGE" in iface_data: ivs_uplinks.append(interface_name) + all_file_names.append(route6_path) if (utils.diff(interface_path, iface_data) or - utils.diff(route_path, route_data)): + utils.diff(route_path, route_data) or + utils.diff(route6_path, route6_data)): restart_interfaces.append(interface_name) restart_interfaces.extend(self.child_members(interface_name)) update_files[interface_path] = iface_data update_files[route_path] = route_data + update_files[route6_path] = route6_data + else: logger.info('No changes required for interface: %s' % interface_name) @@ -412,44 +438,62 @@ class IfcfgNetConfig(os_net_config.NetConfig): for bridge_name, bridge_data in self.bridge_data.iteritems(): route_data = self.route_data.get(bridge_name, '') + route6_data = self.route6_data.get(bridge_name, '') bridge_path = self.root_dir + bridge_config_path(bridge_name) - bridge_route_path = self.root_dir + route_config_path(bridge_name) + br_route_path = self.root_dir + route_config_path(bridge_name) + br_route6_path = self.root_dir + route6_config_path(bridge_name) all_file_names.append(bridge_path) - all_file_names.append(bridge_route_path) + all_file_names.append(br_route_path) + all_file_names.append(br_route6_path) if (utils.diff(bridge_path, bridge_data) or - utils.diff(bridge_route_path, route_data)): + utils.diff(br_route_path, route_data) or + utils.diff(br_route6_path, route6_data)): restart_bridges.append(bridge_name) restart_interfaces.extend(self.child_members(bridge_name)) update_files[bridge_path] = bridge_data - update_files[bridge_route_path] = route_data + update_files[br_route_path] = route_data + update_files[br_route6_path] = route6_data + else: logger.info('No changes required for bridge: %s' % bridge_name) for bridge_name, bridge_data in self.linuxbridge_data.iteritems(): route_data = self.route_data.get(bridge_name, '') + route6_data = self.route6_data.get(bridge_name, '') bridge_path = self.root_dir + bridge_config_path(bridge_name) - bridge_route_path = self.root_dir + route_config_path(bridge_name) + br_route_path = self.root_dir + route_config_path(bridge_name) + br_route6_path = self.root_dir + route6_config_path(bridge_name) all_file_names.append(bridge_path) - all_file_names.append(bridge_route_path) + all_file_names.append(br_route_path) + all_file_names.append(br_route6_path) if (utils.diff(bridge_path, bridge_data) or - utils.diff(bridge_route_path, route_data)): + utils.diff(br_route_path, route_data) or + utils.diff(br_route6_path, route6_data)): restart_bridges.append(bridge_name) restart_interfaces.extend(self.child_members(bridge_name)) update_files[bridge_path] = bridge_data - update_files[bridge_route_path] = route_data + update_files[br_route_path] = route_data + update_files[br_route6_path] = route6_data + else: logger.info('No changes required for bridge: %s' % bridge_name) for bond_name, bond_data in self.linuxbond_data.iteritems(): route_data = self.route_data.get(bond_name, '') + route6_data = self.route6_data.get(bond_name, '') bond_path = self.root_dir + bridge_config_path(bond_name) bond_route_path = self.root_dir + route_config_path(bond_name) + bond_route6_path = self.root_dir + route6_config_path(bond_name) all_file_names.append(bond_path) all_file_names.append(bond_route_path) + all_file_names.append(bond_route6_path) if (utils.diff(bond_path, bond_data) or - utils.diff(bond_route_path, route_data)): + utils.diff(bond_route_path, route_data) or + utils.diff(bond_route6_path, route6_data)): restart_interfaces.append(bond_name) restart_interfaces.extend(self.child_members(bond_name)) update_files[bond_path] = bond_data update_files[bond_route_path] = route_data + update_files[bond_route6_path] = route6_data + else: logger.info('No changes required for linux bond: %s' % bond_name) diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py index bf3ce7c..f92b0a6 100644 --- a/os_net_config/tests/test_impl_ifcfg.py +++ b/os_net_config/tests/test_impl_ifcfg.py @@ -39,6 +39,14 @@ IPADDR=192.168.1.2 NETMASK=255.255.255.0 """ +_V4_V6_IFCFG = _BASE_IFCFG + """IPV6INIT=yes +BOOTPROTO=static +IPADDR=192.168.1.2 +NETMASK=255.255.255.0 +IPV6_AUTOCONF=no +IPV6ADDR=2001:abc:a::/64 +""" + _IFCFG_VLAN = """# This file is autogenerated by os-net-config DEVICE=em1.120 ONBOOT=yes @@ -75,6 +83,11 @@ _ROUTES = """default via 192.168.1.1 dev em1 172.19.0.0/24 via 192.168.1.1 dev em1 """ +_ROUTES_V6 = """default via 2001:db8::1 dev em1 +2001:db8:dead:beef:cafe::/56 via fd00:fd00:2000::1 dev em1 +""" + + _OVS_INTERFACE = _BASE_IFCFG + """DEVICETYPE=ovs TYPE=OVSPort OVS_BRIDGE=br-ctlplane @@ -231,6 +244,9 @@ class TestIfcfgNetConfig(base.TestCase): def get_route_config(self, name='em1'): return self.provider.route_data.get(name, '') + def get_route6_config(self, name='em1'): + return self.provider.route6_data.get(name, '') + def test_add_base_interface(self): interface = objects.Interface('em1') self.provider.add_interface(interface) @@ -304,6 +320,20 @@ class TestIfcfgNetConfig(base.TestCase): self.assertEqual(_V4_IFCFG, self.get_interface_config()) self.assertEqual(_ROUTES, self.get_route_config()) + def test_network_with_ipv6_routes(self): + route1 = objects.Route('192.168.1.1', default=True) + route2 = objects.Route('192.168.1.1', '172.19.0.0/24') + route3 = objects.Route('2001:db8::1', default=True) + route4 = objects.Route('fd00:fd00:2000::1', + '2001:db8:dead:beef:cafe::/56') + v4_addr = objects.Address('192.168.1.2/24') + v6_addr = objects.Address('2001:abc:a::/64') + interface = objects.Interface('em1', addresses=[v4_addr, v6_addr], + routes=[route1, route2, route3, route4]) + self.provider.add_interface(interface) + self.assertEqual(_V4_V6_IFCFG, self.get_interface_config()) + self.assertEqual(_ROUTES_V6, self.get_route6_config()) + def test_network_ovs_bridge_with_dhcp(self): interface = objects.Interface('em1') bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True, @@ -518,6 +548,7 @@ class TestIfcfgNetConfigApply(base.TestCase): super(TestIfcfgNetConfigApply, self).setUp() self.temp_ifcfg_file = tempfile.NamedTemporaryFile() self.temp_route_file = tempfile.NamedTemporaryFile() + self.temp_route6_file = tempfile.NamedTemporaryFile() self.temp_bridge_file = tempfile.NamedTemporaryFile() self.temp_cleanup_file = tempfile.NamedTemporaryFile(delete=False) self.ifup_interface_names = [] @@ -531,6 +562,10 @@ class TestIfcfgNetConfigApply(base.TestCase): return self.temp_route_file.name self.stubs.Set(impl_ifcfg, 'route_config_path', test_routes_path) + def test_routes6_path(name): + return self.temp_route6_file.name + self.stubs.Set(impl_ifcfg, 'route6_config_path', test_routes6_path) + def test_bridge_path(name): return self.temp_bridge_file.name self.stubs.Set(impl_ifcfg, 'bridge_config_path', test_bridge_path) @@ -552,6 +587,7 @@ class TestIfcfgNetConfigApply(base.TestCase): def tearDown(self): self.temp_ifcfg_file.close() self.temp_route_file.close() + self.temp_route6_file.close() self.temp_bridge_file.close() if os.path.exists(self.temp_cleanup_file.name): self.temp_cleanup_file.close() -- cgit 1.2.3-korg