]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/openvswitch_port.py
394ed663f034e2eea60f3eaf830d5c824960f74a
[mirror_ifupdown2.git] / ifupdown2 / addons / openvswitch_port.py
1 #!/usr/bin/env python3
2 #
3 # Copyright 2020 Alexandre Derumier <aderumier@odiso.com>
4 # Author: Alexandre Derumier, aderumier@odiso.com
5 #
6
7 try:
8 from ifupdown2.lib.addon import Addon
9
10 from ifupdown2.ifupdown.iface import *
11 from ifupdown2.ifupdown.utils import utils
12 from ifupdown2.ifupdownaddons.modulebase import moduleBase
13 from ifupdown2.ifupdown.exceptions import moduleNotSupported
14 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
15
16 except:
17 from lib.addon import Addon
18
19 from ifupdown.iface import *
20 from ifupdown.utils import utils
21 from ifupdownaddons.modulebase import moduleBase
22 from ifupdown.exceptions import moduleNotSupported
23 import ifupdown.ifupdownflags as ifupdownflags
24
25 import logging
26 import re
27 import subprocess
28 import os
29
30 class openvswitch_port(Addon, moduleBase):
31 """ ifupdown2 addon module to configure openvswitch ports """
32
33 _modinfo = {
34 'mhelp': 'openvswitch module configure openvswitch ports',
35 'attrs': {
36 'ovs-bridge': {
37 'help': 'Interfaces to be part of this ovs bridge',
38 'required': True,
39 },
40 'ovs-type': {
41 'help': 'ovs interface type',
42 'validvals': ['OVSPort', 'OVSIntPort', 'OVSBond', 'OVSTunnel', 'OVSPatchPort'],
43 'required': True,
44 'example': ['ovs-type OVSPort'],
45 },
46 'ovs-options': {
47 'help': 'This option lets you add extra arguments to a ovs-vsctl command',
48 'required': False,
49 'example': ['ovs_options bond_mode=balance-tcp lacp=active tag=100']
50 },
51 'ovs-extra': {
52 'help': 'This option lets you run additional ovs-vsctl commands,' +
53 'separated by "--" (double dash). Variables can be part of the "ovs_extra"' +
54 'option. You can provide all the standard environmental variables' +
55 'described in the interfaces(5) man page. You can also pass shell' +
56 'commands.extra args',
57 'required': False,
58 'example': ['ovs_extra set interface ${IFACE} external-ids:iface-id=$(hostname -s)']
59 },
60 'ovs-bonds': {
61 'help': 'Interfaces to be part of this ovs bond',
62 'validvals': ['<interface-list>'],
63 'required': False,
64 },
65 'ovs-tunnel-type': {
66 'help': 'For "OVSTunnel" interfaces, the type of the tunnel',
67 'required': False,
68 'example': ['ovs-tunnel-type gre'],
69 },
70 'ovs-tunnel-options': {
71 'help': 'For "OVSTunnel" interfaces, this field should be ' +
72 'used to specify the tunnel options like remote_ip, key, etc.',
73 'required': False,
74 'example': ['ovs-tunnel-options options:remote_ip=182.168.1.2 options:key=1'],
75 },
76 'ovs-patch-peer': {
77 'help': 'ovs patch peer',
78 'required': False,
79 'example': ['ovs-patch-peer patch0'],
80 },
81 'ovs-mtu': {
82 'help': 'mtu of the ovs interface',
83 'required': False,
84 'example': ['ovs-mtu 9000'],
85 },
86 }
87 }
88
89 def __init__ (self, *args, **kargs):
90 moduleBase.__init__ (self, *args, **kargs)
91 Addon.__init__(self)
92 if not os.path.exists('/usr/bin/ovs-vsctl'):
93 raise moduleNotSupported('module init failed: no /usr/bin/ovs-vsctl found')
94
95 def _is_ovs_port (self, ifaceobj):
96 ovstype = ifaceobj.get_attr_value_first ('ovs-type')
97 ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge')
98 if ovstype and ovsbridge:
99 return True
100 return False
101
102 def _get_bond_ifaces (self, ifaceobj):
103 ovs_bonds = ifaceobj.get_attr_value_first ('ovs-bonds')
104 if ovs_bonds:
105 return sorted (ovs_bonds.split ())
106 return None
107
108 def _ovs_vsctl(self, ifaceobj, cmdlist):
109
110 if cmdlist:
111
112 os.environ['IFACE'] = ifaceobj.name if ifaceobj.name else ''
113 os.environ['LOGICAL'] = ifaceobj.name if ifaceobj.name else ''
114 os.environ['METHOD'] = ifaceobj.addr_method if ifaceobj.addr_method else ''
115 os.environ['ADDRFAM'] = ','.join(ifaceobj.addr_family) if ifaceobj.addr_family else ''
116
117 finalcmd = "/usr/bin/ovs-vsctl"
118
119 for cmd in cmdlist:
120 finalcmd = finalcmd + " -- " + cmd
121
122 try:
123 self.logger.debug ("Running %s" % (finalcmd))
124 utils.exec_user_command(finalcmd)
125 except subprocess.CalledProcessError as c:
126 raise Exception ("Command \"%s failed: %s" % (finalcmd, c.output))
127 except Exception as e:
128 raise Exception ("%s" % e)
129
130 def _addport (self, ifaceobj):
131 iface = ifaceobj.name
132 ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge')
133 ovsoptions = ifaceobj.get_attr_value_first ('ovs-options')
134 ovstype = ifaceobj.get_attr_value_first ('ovs-type')
135 ovsbonds = ifaceobj.get_attr_value_first ('ovs-bonds')
136 ovsextra = ifaceobj.get_attr_value('ovs-extra')
137
138 cmd_list = []
139
140 if ovstype == 'OVSBond':
141 if ovsbonds is None:
142 raise Exception ("missing ovs-bonds option")
143 cmd = "--may-exist --fake-iface add-bond %s %s %s"%(ovsbridge, iface, ovsbonds)
144 cmd_list.append(cmd)
145 else:
146 cmd = "--may-exist add-port %s %s"%(ovsbridge, iface)
147 cmd_list.append(cmd)
148
149
150 #clear old ports options
151 cmd = "--if-exists clear port %s bond_active_slave bond_mode cvlans external_ids lacp mac other_config qos tag trunks vlan_mode"%(iface)
152 cmd_list.append(cmd)
153
154 #clear old interface options
155 cmd = "--if-exists clear interface %s mtu_request external-ids other_config options"%(iface)
156 cmd_list.append(cmd)
157
158 if ovsoptions:
159 cmd = "set Port %s %s" %(iface, ovsoptions)
160 cmd_list.append(cmd)
161
162
163 if ovstype == 'OVSIntPort':
164 cmd = "set Interface %s type=internal"%(iface)
165 cmd_list.append(cmd)
166
167 if ovstype == 'OVSTunnel':
168 ovstunneltype = ifaceobj.get_attr_value_first ('ovs-tunnel-type')
169 if ovstunneltype is None:
170 raise Exception ("missing ovs-tunnel-type option")
171 ovstunneloptions = ifaceobj.get_attr_value_first('ovs-tunnel-options')
172 if ovstunneloptions is None:
173 raise Exception ("missing ovs-tunnel-options option")
174 cmd = "set Interface %s type=%s %s"%(iface, ovstunneltype, ovstunneloptions)
175 cmd_list.append(cmd)
176
177 if ovstype == 'OVSPatchPort':
178 ovspatchpeer = ifaceobj.get_attr_value_first ('ovs-patch-peer')
179 if ovspatchpeer is None:
180 raise Exception ("missing ovs-patch-peer")
181 cmd = "set Interface %s type=patch options:peer=%s"%(iface, ovspatchpeer)
182 cmd_list.append(cmd)
183
184 #mtu
185 ovsmtu = ifaceobj.get_attr_value_first ('ovs-mtu')
186 ovsbonds_list = self._get_bond_ifaces(ifaceobj)
187 if ovsmtu is not None:
188 #we can't set mtu on bond fake interface, we apply it on slaves interfaces
189 if ovstype == 'OVSBond' and ovsbonds_list is not None:
190 for slave in ovsbonds_list:
191 cmd = "set Interface %s mtu_request=%s"%(slave,ovsmtu)
192 cmd_list.append(cmd)
193
194 else:
195 cmd = "set Interface %s mtu_request=%s"%(iface,ovsmtu)
196 cmd_list.append(cmd)
197
198 #extra
199 if ovsextra is not None:
200 cmd_list.extend(ovsextra)
201
202 self._ovs_vsctl(ifaceobj, cmd_list)
203
204 def _delport (self, ifaceobj):
205 iface = ifaceobj.name
206 ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge')
207 cmd = "--if-exists del-port %s %s"%(ovsbridge, iface)
208
209 self._ovs_vsctl(ifaceobj, [cmd])
210
211 def get_dependent_ifacenames (self, ifaceobj, ifaceobjs_all=None):
212
213 if not self._is_ovs_port (ifaceobj):
214 return None
215
216 ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge')
217 return [ovsbridge]
218
219 def _up (self, ifaceobj):
220
221 self._addport (ifaceobj)
222
223 def _down (self, ifaceobj):
224 if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists (ifaceobj.name):
225 return
226
227 self._delport (ifaceobj)
228
229 def _query_check (self, ifaceobj, ifaceobjcurr):
230 if not self.cache.link_exists (ifaceobj.name):
231 return
232 return
233
234 _run_ops = {
235 'pre-up': _up,
236 'post-down': _down,
237 'query-checkcurr': _query_check
238 }
239
240 def get_ops (self):
241 """ returns list of ops supported by this module """
242 return self._run_ops.keys ()
243
244 def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
245 """ run Openvswitch port configuration on the interface object passed as argument
246
247 Args:
248 **ifaceobj** (object): iface object
249
250 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
251 'query-running'
252 Kwargs:
253 **query_ifaceobj** (object): query check ifaceobject. This is only
254 valid when op is 'query-checkcurr'. It is an object same as
255 ifaceobj, but contains running attribute values and its config
256 status. The modules can use it to return queried running state
257 of interfaces. status is success if the running state is same
258 as user required state in ifaceobj. error otherwise.
259 """
260 op_handler = self._run_ops.get (operation)
261 if not op_handler:
262 return
263
264 if (operation != 'query-running' and not self._is_ovs_port (ifaceobj)):
265 return
266
267 if operation == 'query-checkcurr':
268 op_handler (self, ifaceobj, query_ifaceobj)
269 else:
270 op_handler (self, ifaceobj)