[pve-devel] [PATCH v2 ifupdown2 2/2] add openvswitch addon
Alexandre Derumier
aderumier at odiso.com
Mon Feb 17 15:59:44 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