From d325a1a2f8d7c815cbaa016858db053412201b9d Mon Sep 17 00:00:00 2001 From: James Slagle Date: Mon, 11 Apr 2016 12:19:51 -0400 Subject: Add support for OVS tunnels Adds support for configuring OVS Tunnels via os-net-config. Tunnels are configured as members of ovs_bridge's where the type is set to ovs_tunnel. The object also supports setting OVS extra and options so that additional tunnel data can be defined, such as remote_ip. Change-Id: I31ac1cbe8a13247a1529c0f99a0aea5807888844 --- os_net_config/__init__.py | 9 ++++++++ os_net_config/impl_ifcfg.py | 18 ++++++++++++++++ os_net_config/objects.py | 37 ++++++++++++++++++++++++++++++++- os_net_config/tests/test_impl_ifcfg.py | 38 ++++++++++++++++++++++++++++++++++ os_net_config/tests/test_objects.py | 34 ++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+), 1 deletion(-) diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py index 745da18..700a09d 100644 --- a/os_net_config/__init__.py +++ b/os_net_config/__init__.py @@ -71,6 +71,8 @@ class NetConfig(object): self.add_linux_bond(obj) for member in obj.members: self.add_object(member) + elif isinstance(obj, objects.OvsTunnel): + self.add_ovs_tunnel(obj) def add_interface(self, interface): """Add an Interface object to the net config object. @@ -121,6 +123,13 @@ class NetConfig(object): """ raise NotImplemented("add_linux_bond is not implemented.") + def add_ovs_tunnel(self, tunnel): + """Add a OvsTunnel object to the net config object. + + :param tunnel: The OvsTunnel object to add. + """ + raise NotImplemented("add_ovs_tunnel is not implemented.") + def apply(self, cleanup=False): """Apply the network configuration. diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py index 6459bcc..f6b4617 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -177,6 +177,14 @@ class IfcfgNetConfig(os_net_config.NetConfig): self.member_names[base_opt.name] = members if base_opt.bonding_options: data += "BONDING_OPTS=\"%s\"\n" % base_opt.bonding_options + elif isinstance(base_opt, objects.OvsTunnel): + ovs_extra.extend(base_opt.ovs_extra) + data += "DEVICETYPE=ovs\n" + data += "TYPE=OVSTunnel\n" + data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name + data += "OVS_TUNNEL_TYPE=%s\n" % base_opt.tunnel_type + data += "OVS_TUNNEL_OPTIONS=\"%s\"\n" % \ + ' '.join(base_opt.ovs_options) else: if base_opt.use_dhcp: data += "BOOTPROTO=dhcp\n" @@ -356,6 +364,16 @@ class IfcfgNetConfig(os_net_config.NetConfig): if bond.routes: self._add_routes(bond.name, bond.routes) + def add_ovs_tunnel(self, tunnel): + """Add a OvsTunnel object to the net config object. + + :param tunnel: The OvsTunnel object to add. + """ + logger.info('adding ovs tunnel: %s' % tunnel.name) + data = self._add_common(tunnel) + logger.debug('ovs tunnel data: %s' % data) + self.interface_data[tunnel.name] = data + def generate_ivs_config(self, ivs_uplinks, ivs_interfaces): """Generate configuration content for ivs.""" diff --git a/os_net_config/objects.py b/os_net_config/objects.py index 4f3e0d5..2db7cf5 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -48,6 +48,8 @@ def object_from_json(json): return IvsBridge.from_json(json) elif obj_type == "ivs_interface": return IvsInterface.from_json(json) + elif obj_type == "ovs_tunnel": + return OvsTunnel.from_json(json) def _get_required_field(json, name, object_name): @@ -343,7 +345,8 @@ class OvsBridge(_BaseOpts): self.ovs_extra = ovs_extra for member in self.members: member.bridge_name = name - member.ovs_port = True + if not isinstance(member, OvsTunnel): + member.ovs_port = True if member.primary: if self.primary_interface_name: msg = 'Only one primary interface allowed per bridge.' @@ -615,3 +618,35 @@ class OvsBond(_BaseOpts): ovs_extra=ovs_extra, nic_mapping=nic_mapping, persist_mapping=persist_mapping, defroute=defroute, dhclient_args=dhclient_args, dns_servers=dns_servers) + + +class OvsTunnel(_BaseOpts): + """Base class for OVS Tunnels.""" + + def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None, + routes=None, mtu=None, primary=False, nic_mapping=None, + persist_mapping=False, defroute=True, dhclient_args=None, + dns_servers=None, tunnel_type=None, ovs_options=None, + ovs_extra=None): + addresses = addresses or [] + routes = routes or [] + ovs_extra = ovs_extra or [] + dns_servers = dns_servers or [] + super(OvsTunnel, self).__init__(name, use_dhcp, use_dhcpv6, addresses, + routes, mtu, primary, nic_mapping, + persist_mapping, defroute, + dhclient_args, dns_servers) + self.tunnel_type = tunnel_type + self.ovs_options = ovs_options or [] + self.ovs_extra = ovs_extra or [] + + @staticmethod + def from_json(json): + name = _get_required_field(json, 'name', 'OvsTunnel') + tunnel_type = _get_required_field(json, 'tunnel_type', 'OvsTunnel') + ovs_options = json.get('ovs_options', []) + ovs_options = ['options:%s' % opt for opt in ovs_options] + ovs_extra = json.get('ovs_extra', []) + opts = _BaseOpts.base_opts_from_json(json) + return OvsTunnel(name, *opts, tunnel_type=tunnel_type, + ovs_options=ovs_options, ovs_extra=ovs_extra) diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py index d68a96e..f4701a0 100644 --- a/os_net_config/tests/test_impl_ifcfg.py +++ b/os_net_config/tests/test_impl_ifcfg.py @@ -76,6 +76,19 @@ _V6_IFCFG_MULTIPLE = (_V6_IFCFG + "IPV6ADDR_SECONDARIES=\"2001:abc:b::1/64 " + _OVS_IFCFG = _BASE_IFCFG + "DEVICETYPE=ovs\nBOOTPROTO=none\n" +_OVS_IFCFG_TUNNEL = """# This file is autogenerated by os-net-config +DEVICE=tun0 +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=no +PEERDNS=no +DEVICETYPE=ovs +TYPE=OVSTunnel +OVS_BRIDGE=br-ctlplane +OVS_TUNNEL_TYPE=gre +OVS_TUNNEL_OPTIONS="options:remote_ip=192.168.1.1" +""" + _OVS_BRIDGE_IFCFG = _BASE_IFCFG + "DEVICETYPE=ovs\n" @@ -287,6 +300,15 @@ class TestIfcfgNetConfig(base.TestCase): self.provider.add_interface(interface) self.assertEqual(_OVS_IFCFG, self.get_interface_config()) + def test_add_ovs_tunnel(self): + interface = objects.OvsTunnel('tun0') + interface.type = 'ovs_tunnel' + interface.tunnel_type = 'gre' + interface.ovs_options = ['options:remote_ip=192.168.1.1'] + interface.bridge_name = 'br-ctlplane' + self.provider.add_interface(interface) + self.assertEqual(_OVS_IFCFG_TUNNEL, self.get_interface_config('tun0')) + def test_add_interface_with_v4(self): v4_addr = objects.Address('192.168.1.2/24') interface = objects.Interface('em1', addresses=[v4_addr]) @@ -389,6 +411,22 @@ class TestIfcfgNetConfig(base.TestCase): self.assertEqual(_OVS_BRIDGE_STATIC, self.provider.bridge_data['br-ctlplane']) + def test_network_ovs_bridge_with_tunnel(self): + interface = objects.OvsTunnel('tun0') + interface.type = 'ovs_tunnel' + interface.tunnel_type = 'gre' + interface.ovs_options = ['options:remote_ip=192.168.1.1'] + interface.bridge_name = 'br-ctlplane' + self.provider.add_interface(interface) + v4_addr = objects.Address('192.168.1.2/24') + bridge = objects.OvsBridge('br-ctlplane', members=[interface], + addresses=[v4_addr]) + self.provider.add_bridge(bridge) + self.provider.add_interface(interface) + self.assertEqual(_OVS_IFCFG_TUNNEL, self.get_interface_config('tun0')) + self.assertEqual(_OVS_BRIDGE_STATIC, + self.provider.bridge_data['br-ctlplane']) + def test_network_linux_bridge_static(self): v4_addr = objects.Address('192.168.1.2/24') interface = objects.Interface('em1') diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py index d3e41df..a7b4230 100644 --- a/os_net_config/tests/test_objects.py +++ b/os_net_config/tests/test_objects.py @@ -514,6 +514,40 @@ class TestLinuxBond(base.TestCase): self.assertEqual("em2", interface2.name) +class TestOvsTunnel(base.TestCase): + + def test_from_json(self): + data = """{ +"type": "ovs_bridge", +"name": "br-foo", +"members": [{ + "type": "ovs_tunnel", + "name": "tun0", + "tunnel_type": "gre", + "ovs_options": [ + "remote_ip=192.168.1.1" + ], + "ovs_extra": [ + "ovs extra" + ] +}] +} +""" + bridge = objects.object_from_json(json.loads(data)) + self.assertEqual("br-foo", bridge.name) + tun0 = bridge.members[0] + self.assertEqual("tun0", tun0.name) + self.assertFalse(tun0.ovs_port) + self.assertEqual("br-foo", tun0.bridge_name) + self.assertEqual("gre", tun0.tunnel_type) + self.assertEqual( + ["options:remote_ip=192.168.1.1"], + tun0.ovs_options) + self.assertEqual( + ["ovs extra"], + tun0.ovs_extra) + + class TestNumberedNicsMapping(base.TestCase): # We want to test the function, not the dummy.. -- cgit 1.2.3-korg