[pve-devel] [PATCH V3 ifupdown2 2/3] add openvswitch addon

Alexandre Derumier aderumier at odiso.com
Fri Feb 21 10:27:13 CET 2020


This reimplement the ifupdown1 script with same options,

with reloading included !
---
 .../pve/0008-add-openvswitch-addon.patch      | 572 ++++++++++++++++++
 debian/patches/series                         |   1 +
 2 files changed, 573 insertions(+)
 create mode 100644 debian/patches/pve/0008-add-openvswitch-addon.patch

diff --git a/debian/patches/pve/0008-add-openvswitch-addon.patch b/debian/patches/pve/0008-add-openvswitch-addon.patch
new file mode 100644
index 0000000..fd8c313
--- /dev/null
+++ b/debian/patches/pve/0008-add-openvswitch-addon.patch
@@ -0,0 +1,572 @@
+From 6d79fb897779792363f8b50a44bfd3b4dee11e15 Mon Sep 17 00:00:00 2001
+From: Alexandre Derumier <aderumier at odiso.com>
+Date: Mon, 17 Feb 2020 13:32:18 +0100
+Subject: [PATCH] add openvswitch addon
+
+---
+ etc/network/ifupdown2/addons.conf    |   4 +
+ ifupdown2/addons/openvswitch.py      | 226 ++++++++++++++++++++++
+ ifupdown2/addons/openvswitch_port.py | 274 +++++++++++++++++++++++++++
+ ifupdown2/lib/iproute2.py            |   3 +
+ ifupdown2/nlmanager/nlpacket.py      |   1 +
+ 5 files changed, 508 insertions(+)
+ create mode 100644 ifupdown2/addons/openvswitch.py
+ create mode 100644 ifupdown2/addons/openvswitch_port.py
+
+diff --git a/etc/network/ifupdown2/addons.conf b/etc/network/ifupdown2/addons.conf
+index e3639a7..99aca90 100644
+--- a/etc/network/ifupdown2/addons.conf
++++ b/etc/network/ifupdown2/addons.conf
+@@ -1,3 +1,5 @@
++pre-up,openvswitch
++pre-up,openvswitch_port
+ pre-up,xfrm
+ pre-up,link
+ pre-up,ppp
+@@ -45,3 +47,5 @@ post-down,usercmds
+ post-down,link
+ post-down,tunnel
+ post-down,xfrm
++post-down,openvswitch_port
++post-down,openvswitch
+diff --git a/ifupdown2/addons/openvswitch.py b/ifupdown2/addons/openvswitch.py
+new file mode 100644
+index 0000000..6369c7e
+--- /dev/null
++++ b/ifupdown2/addons/openvswitch.py
+@@ -0,0 +1,226 @@
++#!/usr/bin/python
++#
++# Copyright 2020 Alexandre Derumier <aderumier at odiso.com>
++# Author: Alexandre Derumier, aderumier at odiso.com
++#
++
++try:
++    from ifupdown2.lib.addon import Addon
++
++    from ifupdown2.ifupdown.iface import *
++    from ifupdown2.ifupdown.utils import utils
++    from ifupdown2.ifupdownaddons.modulebase import moduleBase
++    from ifupdown2.ifupdown.exceptions import moduleNotSupported
++    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
++
++except:
++    from lib.addon import Addon
++
++    from ifupdown.iface import *
++    from ifupdown.utils import utils
++    from ifupdownaddons.modulebase import moduleBase
++    from ifupdown.exceptions import moduleNotSupported
++    import ifupdown.ifupdownflags as ifupdownflags
++
++import logging
++import re
++import subprocess
++import os
++
++class openvswitch(Addon, moduleBase):
++    """  ifupdown2 addon module to configure Openvswitch bridge """
++
++    _modinfo = {
++        'mhelp': 'openvswitch module configure openvswitch bridges',
++        'attrs': {
++            'ovs-ports': {
++                'help': 'Interfaces to be part of this ovs bridge.',
++                'validvals': ['<interface-list>'],
++                'required': False,
++            },
++            'ovs-type': {
++                'help': 'ovs interface type',
++                'validvals': ['OVSBridge'],
++                'required': True,
++            },
++            'ovs-mtu': {
++                'help': 'Interface MTU (maximum transmission unit)',
++                'validrange': ['552', '9216'],
++                'example': ['ovs-mtu 1600'],
++                'default': '1500'
++            },
++            'ovs-options': {
++                'help': 'This option lets you add extra arguments to a ovs-vsctl command',
++                'required': False,
++            },
++            'ovs-extra': {
++                'help': 'This option lets you run additional ovs-vsctl commands,'  +
++                        'separated by "--" (double dash). Variables can be part of the "ovs_extra"' +
++                        'option. You can provide all the standard environmental variables' + 
++                        'described in the interfaces(5) man page. You can also pass shell' +
++                        'commands.extra args',
++                'required': False,
++                'example': ['ovs_extra set bridge ${IFACE} other-config:hwaddr=00:59:cf:9c:84:3a -- br-set-external-id ${IFACE} bridge-id ${IFACE}']
++
++            },
++        }
++    }
++
++    def __init__ (self, *args, **kargs):
++        moduleBase.__init__ (self, *args, **kargs)
++        Addon.__init__(self)
++        if not os.path.exists('/usr/bin/ovs-vsctl'):
++            raise moduleNotSupported('module init failed: no /usr/bin/ovs-vsctl found')
++
++    def _is_ovs_bridge (self, ifaceobj):
++        ovstype = ifaceobj.get_attr_value_first('ovs-type')
++        if ovstype:
++            if ovstype == 'OVSBridge':
++                return True
++            else:
++                return False
++        return False
++
++    def _get_ovs_ports (self, ifaceobj):
++        ovs_ports = ifaceobj.get_attr_value_first('ovs-ports')
++        if ovs_ports:
++            return sorted (ovs_ports.split ())
++        return None
++
++    def _get_running_ovs_ports (self, iface):
++        output = utils.exec_command("/usr/bin/ovs-vsctl list-ports %s" %iface)
++        if output:
++            ovs_ports = sorted(output.splitlines())
++            return ovs_ports
++        return None
++
++    def _ovs_vsctl(self, ifaceobj, cmdlist):
++
++        if cmdlist:
++
++            os.environ['IFACE'] = ifaceobj.name if ifaceobj.name else ''
++            os.environ['LOGICAL'] = ifaceobj.name if ifaceobj.name else ''
++            os.environ['METHOD'] = ifaceobj.addr_method if ifaceobj.addr_method else ''
++            os.environ['ADDRFAM'] = ','.join(ifaceobj.addr_family) if ifaceobj.addr_family else ''
++
++            finalcmd = "/usr/bin/ovs-vsctl"
++
++            for cmd in cmdlist:
++                finalcmd = finalcmd + " -- " + cmd
++
++            try:
++                self.logger.debug ("Running %s" % (finalcmd))
++                utils.exec_user_command(finalcmd)
++            except subprocess.CalledProcessError as c:
++                raise Exception ("Command \"%s failed: %s" % (finalcmd, c.output))
++            except Exception as e:
++                raise Exception ("%s" % e)
++
++    def _addbridge (self, ifaceobj):
++
++        iface = ifaceobj.name
++        ovsoptions = ifaceobj.get_attr_value_first ('ovs-options')
++        ovsextra = ifaceobj.get_attr_value('ovs-extra')
++        ovsmtu = ifaceobj.get_attr_value_first ('ovs-mtu')
++
++        cmd_list = []
++
++        cmd = "--may-exist add-br %s"%(iface)
++        cmd_list.append(cmd)
++
++        if ovsoptions:
++            cmd = "set bridge %s %s" %(iface, ovsoptions)
++            cmd_list.append(cmd)
++
++        #update
++        if self.cache.link_exists (iface):
++            # on update, delete active ports not in the new port list
++            ovs_ports = self._get_ovs_ports(ifaceobj)
++            running_ovs_ports = self._get_running_ovs_ports(iface)
++            if running_ovs_ports is not None and ovs_ports is not None:
++                missingports = list(set(running_ovs_ports) - set(ovs_ports))
++
++            if missingports is not None:
++                for port in missingports:
++                    cmd = "--if-exists del-port %s %s"%(iface, port)
++                    cmd_list.append(cmd)
++
++            #clear old bridge options
++            cmd = "--if-exists clear bridge %s auto_attach controller external-ids fail_mode flood_vlans ipfix mirrors netflow other_config protocols sflow"%(iface)
++
++            cmd_list.append(cmd)
++
++            #clear old interface options
++            cmd = "--if-exists clear interface %s mtu_request external-ids other_config options"%(iface)
++            cmd_list.append(cmd)
++
++        if ovsextra is not None:
++            cmd_list.extend(ovsextra)
++
++        if ovsmtu is not None:
++            cmd = "set Interface %s mtu_request=%s"%(iface, ovsmtu)
++            cmd_list.append(cmd)
++
++        self._ovs_vsctl(ifaceobj, cmd_list)
++        if not self.cache.link_exists(ifaceobj.name):
++            self.iproute2.link_add_openvswitch(ifaceobj.name, "openvswitch")
++
++    def _delbridge (self, ifaceobj):
++
++        cmd = "del-br %s"%(ifaceobj.name)
++        self._ovs_vsctl(ifaceobj, [cmd])
++
++    def get_dependent_ifacenames (self, ifaceobj, ifaceobjs_all=None):
++        return None
++
++    def _up (self, ifaceobj):
++        self._addbridge (ifaceobj)
++
++    def _down (self, ifaceobj):
++        if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists (ifaceobj.name):
++           return
++
++        self._delbridge (ifaceobj)
++
++    def _query_check (self, ifaceobj, ifaceobjcurr):
++        if not self.cache.link_exists (ifaceobj.name):
++            return
++        return
++
++    _run_ops = {
++        'pre-up': _up,
++        'post-down': _down,
++        'query-checkcurr': _query_check
++    }
++
++    def get_ops (self):
++        """ returns list of ops supported by this module """
++        return self._run_ops.keys ()
++
++    def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
++        """ run openvswitch configuration on the interface object passed as argument
++
++        Args:
++            **ifaceobj** (object): iface object
++
++            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
++                                 'query-running'
++        Kwargs:
++            **query_ifaceobj** (object): query check ifaceobject. This is only
++                valid when op is 'query-checkcurr'. It is an object same as
++                ifaceobj, but contains running attribute values and its config
++                status. The modules can use it to return queried running state
++                of interfaces. status is success if the running state is same
++                as user required state in ifaceobj. error otherwise.
++        """
++        op_handler = self._run_ops.get (operation)
++        if not op_handler:
++            return
++
++        if (operation != 'query-running' and not self._is_ovs_bridge (ifaceobj)):
++            return
++
++        if operation == 'query-checkcurr':
++            op_handler (self, ifaceobj, query_ifaceobj)
++        else:
++            op_handler (self, ifaceobj)
+diff --git a/ifupdown2/addons/openvswitch_port.py b/ifupdown2/addons/openvswitch_port.py
+new file mode 100644
+index 0000000..e34cc18
+--- /dev/null
++++ b/ifupdown2/addons/openvswitch_port.py
+@@ -0,0 +1,274 @@
++#!/usr/bin/python
++#
++# Copyright 2020 Alexandre Derumier <aderumier at odiso.com>
++# Author: Alexandre Derumier, aderumier at odiso.com
++#
++
++try:
++    from ifupdown2.lib.addon import Addon
++
++    from ifupdown2.ifupdown.iface import *
++    from ifupdown2.ifupdown.utils import utils
++    from ifupdown2.ifupdownaddons.modulebase import moduleBase
++    from ifupdown2.ifupdown.exceptions import moduleNotSupported
++    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
++
++except:
++    from lib.addon import Addon
++
++    from ifupdown.iface import *
++    from ifupdown.utils import utils
++    from ifupdownaddons.modulebase import moduleBase
++    from ifupdown.exceptions import moduleNotSupported
++    import ifupdown.ifupdownflags as ifupdownflags
++
++import logging
++import re
++import subprocess
++import os
++
++class openvswitch_port(Addon, moduleBase):
++    """  ifupdown2 addon module to configure openvswitch ports """
++
++    _modinfo = {
++        'mhelp': 'openvswitch module configure openvswitch ports',
++        'attrs': {
++            'ovs-bridge': {
++                'help': 'Interfaces to be part of this ovs bridge',
++                'required': True,
++            },
++            'ovs-type': {
++                'help': 'ovs interface type',
++                'validvals': ['OVSPort', 'OVSIntPort', 'OVSBond', 'OVSTunnel', 'OVSPatchPort'],
++                'required': True,
++                'example': ['ovs-type OVSPort'],
++            },
++            'ovs-options': {
++                'help': 'This option lets you add extra arguments to a ovs-vsctl command',
++                'required': False,
++                'example': ['ovs_options bond_mode=balance-tcp lacp=active tag=100']
++            },
++            'ovs-extra': {
++                'help': 'This option lets you run additional ovs-vsctl commands,'  +
++                        'separated by "--" (double dash). Variables can be part of the "ovs_extra"' +
++                        'option. You can provide all the standard environmental variables' + 
++                        'described in the interfaces(5) man page. You can also pass shell' +
++                        'commands.extra args',
++                'required': False,
++                'example': ['ovs_extra set interface ${IFACE} external-ids:iface-id=$(hostname -s)']
++            },
++            'ovs-bonds': {
++                'help': 'Interfaces to be part of this ovs bond',
++                'validvals': ['<interface-list>'],
++                'required': False,
++            },
++            'ovs-tunnel-type': {
++                'help': 'For "OVSTunnel" interfaces, the type of the tunnel',
++                'required': False,
++                'example': ['ovs-tunnel-type gre'],
++            },
++            'ovs-tunnel-options': {
++                'help': 'For "OVSTunnel" interfaces, this field should be ' +
++                        'used to specify the tunnel options like remote_ip, key, etc.',
++                'required': False,
++                'example': ['ovs-tunnel-options options:remote_ip=182.168.1.2 options:key=1'],
++            },
++            'ovs-patch-peer': {
++                'help': 'ovs patch peer',
++                'required': False,
++                'example': ['ovs-patch-peer patch0'],
++            },
++            'ovs-mtu': {
++                'help': 'mtu of the ovs interface',
++                'required': False,
++                'example': ['ovs-mtu 9000'],
++            },
++        }
++    }
++
++    def __init__ (self, *args, **kargs):
++        moduleBase.__init__ (self, *args, **kargs)
++        Addon.__init__(self)
++        if not os.path.exists('/usr/bin/ovs-vsctl'):
++            raise moduleNotSupported('module init failed: no /usr/bin/ovs-vsctl found')
++
++    def _is_ovs_port (self, ifaceobj):
++        ovstype = ifaceobj.get_attr_value_first ('ovs-type')
++        ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge')
++        if ovstype and ovsbridge:
++            return True
++        return False
++
++    def _get_bond_ifaces (self, ifaceobj):
++        ovs_bonds = ifaceobj.get_attr_value_first ('ovs-bonds')
++        if ovs_bonds:
++            return sorted (ovs_bonds.split ())
++        return None
++
++    def _ovs_vsctl(self, ifaceobj, cmdlist):
++
++        if cmdlist:
++
++            os.environ['IFACE'] = ifaceobj.name if ifaceobj.name else ''
++            os.environ['LOGICAL'] = ifaceobj.name if ifaceobj.name else ''
++            os.environ['METHOD'] = ifaceobj.addr_method if ifaceobj.addr_method else ''
++            os.environ['ADDRFAM'] = ','.join(ifaceobj.addr_family) if ifaceobj.addr_family else ''
++
++            finalcmd = "/usr/bin/ovs-vsctl"
++
++            for cmd in cmdlist:
++                finalcmd = finalcmd + " -- " + cmd
++
++            try:
++                self.logger.debug ("Running %s" % (finalcmd))
++                utils.exec_user_command(finalcmd)
++            except subprocess.CalledProcessError as c:
++                raise Exception ("Command \"%s failed: %s" % (finalcmd, c.output))
++            except Exception as e:
++                raise Exception ("%s" % e)
++
++    def _addport (self, ifaceobj):
++        iface = ifaceobj.name
++        ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge')
++        ovsoptions = ifaceobj.get_attr_value_first ('ovs-options')
++        ovstype = ifaceobj.get_attr_value_first ('ovs-type')
++        ovsbonds = ifaceobj.get_attr_value_first ('ovs-bonds')
++        ovsextra = ifaceobj.get_attr_value('ovs-extra')
++
++        cmd_list = []
++
++        if ovstype == 'OVSBond':
++           if ovsbonds is None:
++               raise Exception ("missing ovs-bonds option")
++           cmd = "--may-exist --fake-iface add-bond %s %s %s"%(ovsbridge, iface, ovsbonds)
++           cmd_list.append(cmd)
++        else:
++           cmd = "--may-exist add-port %s %s"%(ovsbridge, iface)
++           cmd_list.append(cmd)
++
++
++        #clear old ports options
++        cmd = "--if-exists clear port %s bond_active_slave bond_mode cvlans external_ids lacp mac other_config qos tag trunks vlan_mode"%(iface)
++        cmd_list.append(cmd)
++
++        #clear old interface options
++        cmd = "--if-exists clear interface %s mtu_request external-ids other_config options"%(iface)
++        cmd_list.append(cmd)
++
++        if ovsoptions:
++            cmd = "set Port %s %s" %(iface, ovsoptions)
++            cmd_list.append(cmd)
++
++
++        if ovstype == 'OVSIntPort':
++            cmd = "set Interface %s type=internal"%(iface)
++            cmd_list.append(cmd)
++
++        if ovstype == 'OVSTunnel':
++            ovstunneltype = ifaceobj.get_attr_value_first ('ovs-tunnel-type')
++            if ovstunneltype is None:
++                raise Exception ("missing ovs-tunnel-type option")
++            ovstunneloptions = ifaceobj.get_attr_value_first('ovs-tunnel-options')
++            if ovstunneloptions is None:
++                raise Exception ("missing ovs-tunnel-options option")
++            cmd = "set Interface %s type=%s %s"%(iface, ovstunneltype, ovstunneloptions)
++            cmd_list.append(cmd)
++
++        if ovstype == 'OVSPatchPort':
++            ovspatchpeer = ifaceobj.get_attr_value_first ('ovs-patch-peer')
++            if ovspatchpeer is None:
++                raise Exception ("missing ovs-patch-peer")
++            cmd = "set Interface %s type=patch options:peer=%s"%(iface, ovspatchpeer)
++            cmd_list.append(cmd)
++
++        #mtu
++        ovsmtu = ifaceobj.get_attr_value_first ('ovs-mtu')
++        ovsbonds_list = self._get_bond_ifaces(ifaceobj)
++        if ovsmtu is not None:
++            #we can't set mtu on bond fake interface, we apply it on slaves interfaces
++            if ovstype == 'OVSBond' and ovsbonds_list is not None:
++                for slave in ovsbonds_list: 
++                    cmd = "set Interface %s mtu_request=%s"%(slave,ovsmtu)
++                    cmd_list.append(cmd)
++
++            else:
++                cmd = "set Interface %s mtu_request=%s"%(iface,ovsmtu)
++                cmd_list.append(cmd)
++
++        #extra
++        if ovsextra is not None:
++            cmd_list.extend(ovsextra)
++
++        self._ovs_vsctl(ifaceobj, cmd_list)
++
++        if ovstype != 'OVSTunnel' and ovstype != 'OVSPatchPort':
++            if not self.cache.link_exists(ifaceobj.name):
++                self.iproute2.link_add_openvswitch(ifaceobj.name, "openvswitch")
++
++    def _delport (self, ifaceobj):
++        iface = ifaceobj.name
++        ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge')
++        cmd = "--if-exists del-port %s %s"%(ovsbridge, iface)
++
++        self._ovs_vsctl(ifaceobj, [cmd])
++
++    def get_dependent_ifacenames (self, ifaceobj, ifaceobjs_all=None):
++
++        if not self._is_ovs_port (ifaceobj):
++            return None
++
++        ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge')
++        return [ovsbridge]
++
++    def _up (self, ifaceobj):
++
++        self._addport (ifaceobj)
++
++    def _down (self, ifaceobj):
++        if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists (ifaceobj.name):
++           return
++
++        self._delport (ifaceobj)
++
++    def _query_check (self, ifaceobj, ifaceobjcurr):
++        if not self.cache.link_exists (ifaceobj.name):
++            return
++        return
++
++    _run_ops = {
++        'pre-up': _up,
++        'post-down': _down,
++        'query-checkcurr': _query_check
++    }
++
++    def get_ops (self):
++        """ returns list of ops supported by this module """
++        return self._run_ops.keys ()
++
++    def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
++        """ run Openvswitch port configuration on the interface object passed as argument
++
++        Args:
++            **ifaceobj** (object): iface object
++
++            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
++                                 'query-running'
++        Kwargs:
++            **query_ifaceobj** (object): query check ifaceobject. This is only
++                valid when op is 'query-checkcurr'. It is an object same as
++                ifaceobj, but contains running attribute values and its config
++                status. The modules can use it to return queried running state
++                of interfaces. status is success if the running state is same
++                as user required state in ifaceobj. error otherwise.
++        """
++        op_handler = self._run_ops.get (operation)
++        if not op_handler:
++            return
++
++        if (operation != 'query-running' and not self._is_ovs_port (ifaceobj)):
++            return
++
++        if operation == 'query-checkcurr':
++            op_handler (self, ifaceobj, query_ifaceobj)
++        else:
++            op_handler (self, ifaceobj)
+diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py
+index 704d120..a1223b9 100644
+--- a/ifupdown2/lib/iproute2.py
++++ b/ifupdown2/lib/iproute2.py
+@@ -334,6 +334,9 @@ class IPRoute2(Cache, Requirements):
+     def link_add_xfrm(ifname, xfrm_name, xfrm_id):
+         utils.exec_commandl(['ip', 'link', 'add', xfrm_name, 'type', 'xfrm', 'dev', ifname, 'if_id', xfrm_id])
+ 
++    def link_add_openvswitch(self, ifname, kind):
++        self.__update_cache_after_link_creation(ifname, kind)
++
+     ############################################################################
+     # TUNNEL
+     ############################################################################
+diff --git a/ifupdown2/nlmanager/nlpacket.py b/ifupdown2/nlmanager/nlpacket.py
+index fcb89fb..c8a0697 100644
+--- a/ifupdown2/nlmanager/nlpacket.py
++++ b/ifupdown2/nlmanager/nlpacket.py
+@@ -2791,6 +2791,7 @@ class AttributeIFLA_LINKINFO(Attribute):
+             "dummy",
+             "bridge",
+             "macvlan",
++            "openvswitch"
+         ):
+             self.log.debug('Unsupported IFLA_INFO_KIND %s' % kind)
+             return
+-- 
+2.20.1
+
diff --git a/debian/patches/series b/debian/patches/series
index 3f39fc8..b8c348c 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -6,3 +6,4 @@ pve/0004-don-t-remove-bridge-is-tap-veth-are-still-plugged.patch
 pve/0005-ifreload-down-up-vxlan-interfaces-when-ifreload_down.patch
 pve/0006-config-tuning.patch
 pve/0007-networking.service-fix-dependencies-and-ordering.patch
+pve/0008-add-openvswitch-addon.patch
-- 
2.20.1



More information about the pve-devel mailing list