]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/batman_adv.py
293d1c34b8dc67f2838f50c962926f676bba3b95
[mirror_ifupdown2.git] / ifupdown2 / addons / batman_adv.py
1 #!/usr/bin/python
2 #
3 # Copyright 2016-2017 Maximilian Wilhelm <max@sdn.clinic>
4 # Author: Maximilian Wilhelm, max@sdn.clinic
5 #
6
7 try:
8 from ifupdown2.ifupdown.iface import *
9 from ifupdown2.ifupdown.utils import utils
10 from ifupdown2.ifupdownaddons.modulebase import moduleBase
11 from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
12 from ifupdown2.ifupdown.netlink import netlink
13 from ifupdown2.ifupdown.exceptions import moduleNotSupported
14 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
15
16 except:
17 from ifupdown.iface import *
18 from ifupdown.utils import utils
19 from ifupdownaddons.modulebase import moduleBase
20 from ifupdownaddons.LinkUtils import LinkUtils
21 from ifupdown.netlink import netlink
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 batman_adv (moduleBase):
31 """ ifupdown2 addon module to configure B.A.T.M.A.N. advanced interfaces """
32
33 _modinfo = {
34 'mhelp' : 'batman_adv module configures B.A.T.M.A.N. advanced interfaces.' +
35 'Every B.A.T.M.A.N. advanced interface needs at least on ethernet ' +
36 'interface to be creatable. You can specify a space separated list' +
37 'of interfaces by using the "batma-ifaces" paramater. If this parameter' +
38 'is set for an interfaces this module will do the magic.',
39
40 'attrs' : {
41 'batman-ifaces' : {
42 'help' : 'Interfaces to be part of this B.A.T.M.A.N. advanced instance',
43 'validvals' : [ '<interface-list>' ],
44 'required' : True,
45 },
46
47 'batman-ifaces-ignore-regex' : {
48 'help' : 'Interfaces to ignore when verifying configuration (regexp)',
49 'required' : False,
50 },
51
52 'batman-distributed-arp-table' : {
53 'help' : 'B.A.T.M.A.N. distributed ARP table',
54 'validvals' : [ 'enabled', 'disabled' ],
55 'required' : False,
56 'batman-attr' : True,
57 },
58
59 'batman-gw-mode' : {
60 'help' : 'B.A.T.M.A.N. gateway mode',
61 'validvals' : [ 'off', 'client', 'server' ],
62 'required' : False,
63 'example' : [ 'batman-gw-mode client' ],
64 'batman-attr' : True,
65 },
66
67 'batman-hop-penalty' : {
68 'help' : 'B.A.T.M.A.N. hop penalty',
69 'validvals' : [ '<number>' ],
70 'required' : False,
71 'batman-attr' : True,
72 },
73
74 'batman-multicast-mode' : {
75 'help' : 'B.A.T.M.A.N. multicast mode',
76 'validvals' : [ 'enabled', 'disabled' ],
77 'required' : False,
78 'batman-attr' : True,
79 },
80 }
81 }
82
83 _batman_attrs = {
84 }
85
86
87 def __init__ (self, *args, **kargs):
88 moduleBase.__init__ (self, *args, **kargs)
89 if not os.path.exists('/usr/sbin/batctl'):
90 raise moduleNotSupported('module init failed: no /usr/sbin/batctl found')
91 self.ipcmd = None
92
93 for longname, entry in self._modinfo['attrs'].items ():
94 if entry.get ('batman-attr', False) == False:
95 continue
96
97 attr = longname.replace ("batman-", "")
98 self._batman_attrs[attr] = {
99 'filename' : attr.replace ("-", "_"),
100 }
101
102
103 def _is_batman_device (self, ifaceobj):
104 if ifaceobj.get_attr_value_first ('batman-ifaces'):
105 return True
106 return False
107
108
109 def _get_batman_ifaces (self, ifaceobj ):
110 batman_ifaces = ifaceobj.get_attr_value_first ('batman-ifaces')
111 if batman_ifaces:
112 return sorted (batman_ifaces.split ())
113 return None
114
115
116 def _get_batman_ifaces_ignore_regex (self, ifaceobj):
117 ifaces_ignore_regex = ifaceobj.get_attr_value_first ('batman-ifaces-ignore-regex')
118 if ifaces_ignore_regex:
119 return re.compile (r"%s" % ifaces_ignore_regex)
120 return None
121
122
123 def _get_batman_attr (self, ifaceobj, attr):
124 if attr not in self._batman_attrs:
125 raise ValueError ("_get_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
126
127 value = ifaceobj.get_attr_value_first ('batman-%s' % attr)
128 if value:
129 return value
130
131 return None
132
133
134 def _read_current_batman_attr (self, ifaceobj, attr):
135 if attr not in self._batman_attrs:
136 raise ValueError ("_read_current_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
137
138 attr_file_name = self._batman_attrs[attr]['filename']
139 attr_file_path = "/sys/class/net/%s/mesh/%s" % (ifaceobj.name, attr_file_name)
140 try:
141 return self.read_file_oneline(attr_file_path)
142 except IOError as i:
143 raise Exception ("_read_current_batman_attr (%s) %s" % (attr, i))
144 except ValueError:
145 raise Exception ("_read_current_batman_attr: Integer value expected, got: %s" % value)
146
147
148 def _set_batman_attr (self, ifaceobj, attr, value):
149 if attr not in self._batman_attrs:
150 raise ValueError ("_set_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
151
152 attr_file_name = self._batman_attrs[attr]['filename']
153 attr_file_path = "/sys/class/net/%s/mesh/%s" % (ifaceobj.name, attr_file_name)
154 try:
155 self.write_file(attr_file_path, "%s\n" % value)
156 except IOError as i:
157 raise Exception ("_set_batman_attr (%s): %s" % (attr, i))
158
159
160 def _batctl_if (self, bat_iface, mesh_iface, op):
161 if op not in [ 'add', 'del' ]:
162 raise Exception ("_batctl_if() called with invalid \"op\" value: %s" % op)
163
164 try:
165 self.logger.debug ("Running batctl -m %s if %s %s" % (bat_iface, op, mesh_iface))
166 utils.exec_commandl(["batctl", "-m", bat_iface, "if", op, mesh_iface])
167 except subprocess.CalledProcessError as c:
168 raise Exception ("Command \"batctl -m %s if %s %s\" failed: %s" % (bat_iface, op, mesh_iface, c.output))
169 except Exception as e:
170 raise Exception ("_batctl_if: %s" % e)
171
172
173 def _find_member_ifaces (self, ifaceobj, ignore = True):
174 members = []
175 iface_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
176 self.logger.info("batman: executing: %s" % " ".join(["batctl", "-m", ifaceobj.name, "if"]))
177 batctl_fh = subprocess.Popen (["batctl", "-m", ifaceobj.name, "if"], bufsize = 4194304, stdout = subprocess.PIPE).stdout
178 for line in batctl_fh.readlines ():
179 iface = line.split (':')[0]
180 if iface_ignore_re and iface_ignore_re.match (iface) and ignore:
181 continue
182
183 members.append (iface)
184
185 return sorted (members)
186
187
188 def get_dependent_ifacenames (self, ifaceobj, ifaceobjs_all=None):
189 if not self._is_batman_device (ifaceobj):
190 return None
191
192 ifaceobj.link_kind |= ifaceLinkKind.BATMAN_ADV
193 batman_ifaces = self._get_batman_ifaces (ifaceobj)
194 if batman_ifaces:
195 return batman_ifaces
196
197 return [None]
198
199
200 def _up (self, ifaceobj):
201 if self._get_batman_ifaces (ifaceobj) == None:
202 raise Exception ('could not determine batman interfacaes')
203
204 # Verify existance of batman interfaces (should be present already)
205 batman_ifaces = []
206 for iface in self._get_batman_ifaces (ifaceobj):
207 if not self.ipcmd.link_exists (iface):
208 self.logger.warn ('batman iface %s not present' % iface)
209 continue
210
211 batman_ifaces.append (iface)
212
213 if len (batman_ifaces) == 0:
214 raise Exception ("None of the configured batman interfaces are available!")
215
216 if_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
217 # Is the batman main interface already present?
218 if self.ipcmd.link_exists (ifaceobj.name):
219 # Verify which member interfaces are present
220 members = self._find_member_ifaces (ifaceobj)
221 for iface in members:
222 if iface not in batman_ifaces:
223 self._batctl_if (ifaceobj.name, iface, 'del')
224 for iface in batman_ifaces:
225 if iface not in members:
226 self._batctl_if (ifaceobj.name, iface, 'add')
227
228 # Batman interfaces no present, add member interfaces to create it
229 else:
230 for iface in batman_ifaces:
231 self._batctl_if (ifaceobj.name, iface, 'add')
232
233 # Check/set any B.A.T.M.A.N. adv. set within interface configuration
234 for attr in self._batman_attrs:
235 value_cfg = self._get_batman_attr (ifaceobj, attr)
236 if value_cfg and value_cfg != self._read_current_batman_attr (ifaceobj, attr):
237 self._set_batman_attr (ifaceobj, attr, value_cfg)
238
239 if ifaceobj.addr_method == 'manual':
240 netlink.link_set_updown(ifaceobj.name, "up")
241
242
243
244 def _down (self, ifaceobj):
245 if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists (ifaceobj.name):
246 return
247
248 members = self._find_member_ifaces (ifaceobj)
249 for iface in members:
250 self._batctl_if (ifaceobj.name, iface, 'del')
251
252 # The main interface will automagically vanish after the last member
253 # interface has been deleted.
254
255
256 def _query_check (self, ifaceobj, ifaceobjcurr):
257 if not self.ipcmd.link_exists (ifaceobj.name):
258 return
259
260 batman_ifaces_cfg = self._get_batman_ifaces (ifaceobj)
261 batman_ifaces_real = self._find_member_ifaces (ifaceobj, False)
262 # Produce list of all current interfaces, tag interfaces ignored by
263 # regex with () around the iface name.
264 batman_ifaces_real_tagged = []
265 iface_ignore_re_str = ifaceobj.get_attr_value_first ('batman-ifaces-ignore-regex')
266 iface_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
267
268 # Assume everything's fine and wait for reality to prove us otherwise
269 ifaces_ok = 0
270
271 # Interfaces configured but not active?
272 for iface in batman_ifaces_cfg:
273 if iface not in batman_ifaces_real:
274 ifaces_ok = 1
275
276 # Interfaces active but not configured (or ignored)?
277 for iface in batman_ifaces_real:
278 if iface not in batman_ifaces_cfg:
279 if iface_ignore_re and iface_ignore_re.match (iface):
280 batman_ifaces_real_tagged.append ("(%s)" % iface)
281 continue
282 ifaces_ok = 1
283 else:
284 batman_ifaces_real_tagged.append (iface)
285
286 # Produce sorted list of active and ignored interfaces
287 ifaces_str = " ".join (batman_ifaces_real_tagged)
288 ifaceobjcurr.update_config_with_status ('batman-ifaces', ifaces_str, ifaces_ok)
289 ifaceobjcurr.update_config_with_status ('batman-ifaces-ignore-regex', iface_ignore_re_str, 0)
290
291 # Check any B.A.T.M.A.N. adv. set within interface configuration
292 for attr in self._batman_attrs:
293 value_cfg = self._get_batman_attr (ifaceobj, attr)
294 value_curr = self._read_current_batman_attr (ifaceobj, attr)
295
296 # Ignore this attribute if its'nt configured for this interface
297 if not value_cfg:
298 continue
299
300 value_ok = 0
301 if value_cfg != value_curr:
302 value_ok = 1
303
304 ifaceobjcurr.update_config_with_status ('batman-%s' % attr, value_curr, value_ok)
305
306
307 def _query_running (self, ifaceobjrunning):
308 if not self.ipcmd.link_exists (ifaceobjrunning.name):
309 return
310
311 # XXX Now what?
312
313
314 _run_ops = {'pre-up' : _up,
315 'post-down' : _down,
316 'query-checkcurr' : _query_check}
317 # XXX 'query-running' : _query_running}
318
319
320 def get_ops (self):
321 """ returns list of ops supported by this module """
322 return self._run_ops.keys ()
323
324
325 def _init_command_handlers (self):
326 if not self.ipcmd:
327 self.ipcmd = LinkUtils()
328
329
330 def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
331 """ run B.A.T.M.A.N. configuration on the interface object passed as argument
332
333 Args:
334 **ifaceobj** (object): iface object
335
336 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
337 'query-running'
338 Kwargs:
339 **query_ifaceobj** (object): query check ifaceobject. This is only
340 valid when op is 'query-checkcurr'. It is an object same as
341 ifaceobj, but contains running attribute values and its config
342 status. The modules can use it to return queried running state
343 of interfaces. status is success if the running state is same
344 as user required state in ifaceobj. error otherwise.
345 """
346 op_handler = self._run_ops.get (operation)
347 if not op_handler:
348 return
349
350 if (operation != 'query-running' and not self._is_batman_device (ifaceobj)):
351 return
352
353 self._init_command_handlers ()
354
355 if operation == 'query-checkcurr':
356 op_handler (self, ifaceobj, query_ifaceobj)
357 else:
358 op_handler (self, ifaceobj)