diff --git a/etc/os-net-config/samples/ovs_patch_port.json b/etc/os-net-config/samples/ovs_patch_port.json new file mode 100644 index 00000000..d4573717 --- /dev/null +++ b/etc/os-net-config/samples/ovs_patch_port.json @@ -0,0 +1,37 @@ +{ + "network_config": [ + { + "name": "br-ctlplane", + "type": "ovs_bridge", + "members": [ + { + "mtu": 1500, + "primary": "true", + "name": "eth1", + "type": "interface" + } + ], + "ovs_extra": [ + "br-set-external-id br-ctlplane bridge-id br-ctlplane" + ], + "mtu": 1500, + "addresses": [ + { + "ip_netmask": "192.0.2.1/24" + } + ] + }, + { + "name": "br_pub-patch", + "type": "ovs_patch_port", + "bridge_name": "br-ctlplane", + "peer": "br-ctlplane-patch" + }, + { + "name": "br-ctlplane-patch", + "type": "ovs_patch_port", + "bridge_name": "br_pub", + "peer": "br_pub-patch" + } + ] +} diff --git a/etc/os-net-config/samples/ovs_patch_port.yaml b/etc/os-net-config/samples/ovs_patch_port.yaml new file mode 100644 index 00000000..91858be5 --- /dev/null +++ b/etc/os-net-config/samples/ovs_patch_port.yaml @@ -0,0 +1,23 @@ +network_config: + - + type: ovs_bridge + name: br-ctlplane + mtu: 1500 + members: + - + type: interface + name: eth1 + # force the MAC address of the bridge to this interface + primary: true + mtu: 1500 + ovs_extra: "br-set-external-id br-ctlplane bridge-id br-ctlplane" + - + type: ovs_patch_port + name: br_pub-patch + bridge_name: br-ctlplane + peer: br-ctlplane-patch + - + type: ovs_patch_port + name: br-ctlplane-patch + bridge_name: br_pub + peer: br_pub-patch diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py index 700a09d2..88e8900b 100644 --- a/os_net_config/__init__.py +++ b/os_net_config/__init__.py @@ -73,6 +73,8 @@ class NetConfig(object): self.add_object(member) elif isinstance(obj, objects.OvsTunnel): self.add_ovs_tunnel(obj) + elif isinstance(obj, objects.OvsPatchPort): + self.add_ovs_patch_port(obj) def add_interface(self, interface): """Add an Interface object to the net config object. @@ -130,6 +132,13 @@ class NetConfig(object): """ raise NotImplemented("add_ovs_tunnel is not implemented.") + def add_ovs_patch_port(self, ovs_patch_port): + """Add a OvsPatchPort object to the net config object. + + :param ovs_patch_port: The OvsPatchPort object to add. + """ + raise NotImplemented("add_ovs_patch_port 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 f6b46170..fbd1c3a5 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -185,11 +185,18 @@ class IfcfgNetConfig(os_net_config.NetConfig): data += "OVS_TUNNEL_TYPE=%s\n" % base_opt.tunnel_type data += "OVS_TUNNEL_OPTIONS=\"%s\"\n" % \ ' '.join(base_opt.ovs_options) + elif isinstance(base_opt, objects.OvsPatchPort): + ovs_extra.extend(base_opt.ovs_extra) + data += "DEVICETYPE=ovs\n" + data += "TYPE=OVSPatchPort\n" + data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name + data += "OVS_PATCH_PEER=%s\n" % base_opt.peer else: if base_opt.use_dhcp: data += "BOOTPROTO=dhcp\n" elif not base_opt.addresses: data += "BOOTPROTO=none\n" + if base_opt.mtu: data += "MTU=%i\n" % base_opt.mtu if base_opt.use_dhcpv6 or base_opt.v6_addresses(): @@ -374,6 +381,16 @@ class IfcfgNetConfig(os_net_config.NetConfig): logger.debug('ovs tunnel data: %s' % data) self.interface_data[tunnel.name] = data + def add_ovs_patch_port(self, ovs_patch_port): + """Add a OvsPatchPort object to the net config object. + + :param ovs_patch_port: The OvsPatchPort object to add. + """ + logger.info('adding ovs patch port: %s' % ovs_patch_port.name) + data = self._add_common(ovs_patch_port) + logger.debug('ovs patch port data: %s' % data) + self.interface_data[ovs_patch_port.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 2958f849..98158324 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -50,6 +50,8 @@ def object_from_json(json): return IvsInterface.from_json(json) elif obj_type == "ovs_tunnel": return OvsTunnel.from_json(json) + elif obj_type == "ovs_patch_port": + return OvsPatchPort.from_json(json) def _get_required_field(json, name, object_name): @@ -652,3 +654,38 @@ class OvsTunnel(_BaseOpts): opts = _BaseOpts.base_opts_from_json(json) return OvsTunnel(name, *opts, tunnel_type=tunnel_type, ovs_options=ovs_options, ovs_extra=ovs_extra) + + +class OvsPatchPort(_BaseOpts): + """Base class for OVS Patch Ports.""" + + 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, bridge_name=None, peer=None, + ovs_options=None, ovs_extra=None): + addresses = addresses or [] + routes = routes or [] + ovs_extra = ovs_extra or [] + dns_servers = dns_servers or [] + super(OvsPatchPort, self).__init__(name, use_dhcp, use_dhcpv6, + addresses, routes, mtu, primary, + nic_mapping, persist_mapping, + defroute, dhclient_args, + dns_servers) + self.bridge_name = bridge_name + self.peer = peer + self.ovs_options = ovs_options or [] + self.ovs_extra = ovs_extra or [] + + @staticmethod + def from_json(json): + name = _get_required_field(json, 'name', 'OvsPatchPort') + bridge_name = _get_required_field(json, 'bridge_name', 'OvsPatchPort') + peer = _get_required_field(json, 'peer', 'OvsPatchPort') + 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 OvsPatchPort(name, *opts, bridge_name=bridge_name, peer=peer, + 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 f4701a01..71ad9aee 100644 --- a/os_net_config/tests/test_impl_ifcfg.py +++ b/os_net_config/tests/test_impl_ifcfg.py @@ -258,6 +258,18 @@ NETMASK=255.255.255.0 _IVS_CONFIG = ('DAEMON_ARGS=\"--hitless --certificate /etc/ivs ' '--inband-vlan 4092 -u em1 --internal-port=storage5\"') +_OVS_IFCFG_PATCH_PORT = """# This file is autogenerated by os-net-config +DEVICE=br-pub-patch +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=no +PEERDNS=no +DEVICETYPE=ovs +TYPE=OVSPatchPort +OVS_BRIDGE=br-ex +OVS_PATCH_PEER=br-ex-patch +""" + class TestIfcfgNetConfig(base.TestCase): @@ -309,6 +321,15 @@ class TestIfcfgNetConfig(base.TestCase): self.provider.add_interface(interface) self.assertEqual(_OVS_IFCFG_TUNNEL, self.get_interface_config('tun0')) + def test_add_ovs_patch_port(self): + patch_port = objects.OvsPatchPort("br-pub-patch") + patch_port.type = 'ovs_patch_port' + patch_port.bridge_name = 'br-ex' + patch_port.peer = 'br-ex-patch' + self.provider.add_interface(patch_port) + self.assertEqual(_OVS_IFCFG_PATCH_PORT, + self.get_interface_config('br-pub-patch')) + def test_add_interface_with_v4(self): v4_addr = objects.Address('192.168.1.2/24') interface = objects.Interface('em1', addresses=[v4_addr]) diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py index 3367df04..7d8d3b31 100644 --- a/os_net_config/tests/test_objects.py +++ b/os_net_config/tests/test_objects.py @@ -548,6 +548,22 @@ class TestOvsTunnel(base.TestCase): tun0.ovs_extra) +class TestOvsPatchPort(base.TestCase): + + def test_from_json(self): + data = """{ +"type": "ovs_patch_port", +"name": "br-pub-patch", +"bridge_name": "br-ex", +"peer": "br-ex-patch" +} +""" + patch_port = objects.object_from_json(json.loads(data)) + self.assertEqual("br-pub-patch", patch_port.name) + self.assertEqual("br-ex", patch_port.bridge_name) + self.assertEqual("br-ex-patch", patch_port.peer) + + class TestNumberedNicsMapping(base.TestCase): # We want to test the function, not the dummy..