From d656cbc483220b94a55b0359815e24c604c93f5c Mon Sep 17 00:00:00 2001 From: marios Date: Mon, 16 Jun 2014 18:22:33 +0300 Subject: Initial commit for basic ENI Debian/Ubuntu network config This uses /etc/network/interfaces single file format. Includes basic tests --- os_net_config/impl_eni.py | 132 ++++++++++++++++++++++++++++ os_net_config/tests/test_impl_eni.py | 162 +++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 os_net_config/impl_eni.py create mode 100644 os_net_config/tests/test_impl_eni.py diff --git a/os_net_config/impl_eni.py b/os_net_config/impl_eni.py new file mode 100644 index 0000000..79671d0 --- /dev/null +++ b/os_net_config/impl_eni.py @@ -0,0 +1,132 @@ + +# -*- coding: utf-8 -*- + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import netaddr +import os_net_config +from os_net_config import utils + +from os_net_config.openstack.common import processutils + + +# TODO(?): should move to interfaces.d +def _network_config_path(): + return "/etc/network/interfaces" + + +class ENINetConfig(os_net_config.NetConfig): + """Ubuntu/Debian implementation for network config + + Configure iface/bridge/routes using debian/ubuntu + /etc/network/interfaces format. + """ + + def __init__(self): + self.interfaces = {} + self.routes = {} + self.bridges = {} + + def _addCommon(self, interface): + + def _static_addresses(_v4, _v6): + addr = _v4 + _v6 + address_data = "" + if addr: + address_data += "address %s\n" % addr[0].ip + address_data += "netmask %s\n" % addr[0].netmask + return address_data + + _v6 = interface.v6_addresses() + _v4 = interface.v4_addresses() + _iface = "iface %s " % interface.name + if _v6: + _iface += "inet6 " + else: + _iface += "inet " + if interface.use_dhcp: + _iface += "dhcp\n" + elif interface.addresses: + _iface += "static\n" + else: + _iface += "manual\n" + data = "" + if interface.type == 'ovs_bridge': + data += "allow-ovs %s\n" % interface.name + data += _iface + data += _static_addresses(_v4, _v6) + data += "ovs_type OVSBridge\n" + if interface.members: + data += "ovs_ports" + for i in interface.members: + data += " %s" % i.name + data += "\n" + elif interface.type == 'ovs_port': + data += "allow-%s %s\n" % (interface.bridge_name, interface.name) + data += _iface + data += _static_addresses(_v4, _v6) + data += "ovs_bridge %s\n" % interface.bridge_name + data += "ovs_type OVSPort\n" + else: + data += "auto %s\n" % interface.name + data += _iface + data += _static_addresses(_v4, _v6) + return data + + def addInterface(self, interface): + data = self._addCommon(interface) + + self.interfaces[interface.name] = data + if interface.routes: + self._addRoutes(interface.name, interface.routes) + + def addBridge(self, bridge): + data = self._addCommon(bridge) + + self.bridges[bridge.name] = data + if bridge.routes: + self._addRoutes(bridge.name, bridge.routes) + if bridge.routes: + self._addRoutes(bridge.name, bridge.routes) + + def _addRoutes(self, interface_name, routes=[]): + data = "" + for route in routes: + rt = netaddr.IPNetwork(route.ip_netmask) + data += "up route add -net %s netmask %s gw %s\n" % ( + str(rt.ip), str(rt.netmask), route.next_hop) + data += "down route del -net %s netmask %s gw %s\n" % ( + str(rt.ip), str(rt.netmask), route.next_hop) + self.routes[interface_name] = data + + def apply(self): + new_config = "" + for interface_name, iface_data in self.interfaces.iteritems(): + route_data = self.routes.get(interface_name) + iface_data += (route_data or '') + new_config += iface_data + + for bridge_name, bridge_data in self.bridges.iteritems(): + route_data = self.routes.get(bridge_name) + bridge_data += (route_data or '') + new_config += bridge_data + if (utils.diff(_network_config_path(), new_config)): + for interface in self.interfaces.keys(): + processutils.execute('/sbin/ifdown', interface, + check_exit_code=False) + + for bridge in self.bridges.keys(): + processutils.execute('/sbin/ifdown', bridge, + check_exit_code=False) + + utils.write_config(_network_config_path(), new_config) diff --git a/os_net_config/tests/test_impl_eni.py b/os_net_config/tests/test_impl_eni.py new file mode 100644 index 0000000..ff4f827 --- /dev/null +++ b/os_net_config/tests/test_impl_eni.py @@ -0,0 +1,162 @@ + +# -*- coding: utf-8 -*- + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tempfile + +from os_net_config import impl_eni +from os_net_config import objects +from os_net_config.openstack.common import processutils +from os_net_config.tests import base +from os_net_config import utils + +_AUTO = "auto eth0\n" + +_BASE_IFACE = "iface eth0" + +_v4_IFACE_NO_IP = _AUTO + _BASE_IFACE + " inet manual\n" + +_V4_IFACE_STATIC_IP = _AUTO + _BASE_IFACE + """ inet static +address 192.168.1.2 +netmask 255.255.255.0 +""" + +_V6_IFACE_STATIC_IP = _AUTO + _BASE_IFACE + """ inet6 static +address fe80::2677:3ff:fe7d:4c +netmask ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff +""" + +_OVS_PORT_BASE = "allow-br0 eth0\n" + +_OVS_IFACE_DHCP = _BASE_IFACE + " inet dhcp\n" + +_OVS_PORT_IFACE = _OVS_PORT_BASE + _OVS_IFACE_DHCP + """ovs_bridge br0 +ovs_type OVSPort\n""" + +_OVS_BRIDGE_DHCP = """allow-ovs br0 +iface br0 inet dhcp +ovs_type OVSBridge +ovs_ports eth0 +""" + +_RTS = """up route add -net 172.19.0.0 netmask 255.255.255.0 gw 192.168.1.1 +down route del -net 172.19.0.0 netmask 255.255.255.0 gw 192.168.1.1 +""" + + +class TestENINetConfig(base.TestCase): + + def setUp(self): + super(TestENINetConfig, self).setUp() + + self.provider = impl_eni.ENINetConfig() + self.if_name = 'eth0' + + def tearDown(self): + super(TestENINetConfig, self).tearDown() + + def get_interface_config(self): + return self.provider.interfaces[self.if_name] + + def get_route_config(self): + return self.provider.routes[self.if_name] + + def _default_interface(self, addr=[], rts=[]): + return objects.Interface(self.if_name, addresses=addr, routes=rts) + + def test_add_base_interface(self): + interface = self._default_interface() + self.provider.addInterface(interface) + self.assertEqual(_v4_IFACE_NO_IP, self.get_interface_config()) + + def test_add_ovs_port_interface(self): + interface = self._default_interface() + interface.type = 'ovs_port' + interface.bridge_name = 'br0' + interface.use_dhcp = True + self.provider.addInterface(interface) + self.assertEqual(_OVS_PORT_IFACE, self.get_interface_config()) + + def test_add_interface_with_v4(self): + v4_addr = objects.Address('192.168.1.2/24') + interface = self._default_interface([v4_addr]) + self.provider.addInterface(interface) + self.assertEqual(_V4_IFACE_STATIC_IP, self.get_interface_config()) + + def test_add_interface_with_v6(self): + v6_addr = objects.Address('fe80::2677:3ff:fe7d:4c') + interface = self._default_interface([v6_addr]) + self.provider.addInterface(interface) + self.assertEqual(_V6_IFACE_STATIC_IP, self.get_interface_config()) + + def test_network_with_routes(self): + route1 = objects.Route('192.168.1.1', '172.19.0.0/24') + v4_addr = objects.Address('192.168.1.2/24') + interface = self._default_interface([v4_addr], [route1]) + self.provider.addInterface(interface) + self.assertEqual(_V4_IFACE_STATIC_IP, self.get_interface_config()) + self.assertEqual(_RTS, self.get_route_config()) + + def test_network_ovs_bridge_with_dhcp(self): + interface = self._default_interface() + interface.use_dhcp = True + bridge = objects.OvsBridge('br0', use_dhcp=True, + members=[interface]) + self.provider.addBridge(bridge) + self.provider.addInterface(interface) + self.assertEqual(_OVS_PORT_IFACE, self.get_interface_config()) + self.assertEqual(_OVS_BRIDGE_DHCP, self.provider.bridges['br0']) + + +class TestENINetConfigApply(base.TestCase): + + def setUp(self): + super(TestENINetConfigApply, self).setUp() + self.temp_config_file = tempfile.NamedTemporaryFile() + + def test_config_path(): + return self.temp_config_file.name + self.stubs.Set(impl_eni, '_network_config_path', test_config_path) + + def test_execute(*args, **kwargs): + pass + self.stubs.Set(processutils, 'execute', test_execute) + + self.provider = impl_eni.ENINetConfig() + + def tearDown(self): + self.temp_config_file.close() + super(TestENINetConfigApply, self).tearDown() + + def test_network_apply(self): + route = objects.Route('192.168.1.1', '172.19.0.0/24') + v4_addr = objects.Address('192.168.1.2/24') + interface = objects.Interface('eth0', addresses=[v4_addr], + routes=[route]) + self.provider.addInterface(interface) + + self.provider.apply() + iface_data = utils.get_file_data(self.temp_config_file.name) + self.assertEqual((_V4_IFACE_STATIC_IP + _RTS), iface_data) + + def test_dhcp_ovs_bridge_network_apply(self): + interface = objects.Interface('eth0') + interface.use_dhcp = True + bridge = objects.OvsBridge('br0', use_dhcp=True, + members=[interface]) + self.provider.addInterface(interface) + self.provider.addBridge(bridge) + self.provider.apply() + iface_data = utils.get_file_data(self.temp_config_file.name) + self.assertEqual((_OVS_PORT_IFACE + _OVS_BRIDGE_DHCP), iface_data) -- cgit 1.2.3-korg