3 # Copyright 2016-2017 Maximilian Wilhelm <max@sdn.clinic>
4 # Author: Maximilian Wilhelm, max@sdn.clinic
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
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
30 class batman_adv (moduleBase
):
31 """ ifupdown2 addon module to configure B.A.T.M.A.N. advanced interfaces """
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.',
42 'help' : 'Interfaces to be part of this B.A.T.M.A.N. advanced instance',
43 'validvals' : [ '<interface-list>' ],
47 'batman-ifaces-ignore-regex' : {
48 'help' : 'Interfaces to ignore when verifying configuration (regexp)',
52 'batman-distributed-arp-table' : {
53 'help' : 'B.A.T.M.A.N. distributed ARP table',
54 'validvals' : [ 'enabled', 'disabled' ],
60 'help' : 'B.A.T.M.A.N. gateway mode',
61 'validvals' : [ 'off', 'client', 'server' ],
63 'example' : [ 'batman-gw-mode client' ],
67 'batman-hop-penalty' : {
68 'help' : 'B.A.T.M.A.N. hop penalty',
69 'validvals' : [ '<number>' ],
74 'batman-multicast-mode' : {
75 'help' : 'B.A.T.M.A.N. multicast mode',
76 'validvals' : [ 'enabled', 'disabled' ],
81 'batman-routing-algo' : {
82 'help' : 'B.A.T.M.A.N. routing algo',
83 'validvals' : [ 'BATMAN_IV', 'BATMAN_V' ],
85 'batman-attr' : False,
94 def __init__ (self
, *args
, **kargs
):
95 moduleBase
.__init
__ (self
, *args
, **kargs
)
96 if not os
.path
.exists('/usr/sbin/batctl'):
97 raise moduleNotSupported('module init failed: no /usr/sbin/batctl found')
100 for longname
, entry
in self
._modinfo
['attrs'].items ():
101 if entry
.get ('batman-attr', False) == False:
104 attr
= longname
.replace ("batman-", "")
105 self
._batman
_attrs
[attr
] = {
106 'filename' : attr
.replace ("-", "_"),
110 def _is_batman_device (self
, ifaceobj
):
111 if ifaceobj
.get_attr_value_first ('batman-ifaces'):
116 def _get_batman_ifaces (self
, ifaceobj
):
117 batman_ifaces
= ifaceobj
.get_attr_value_first ('batman-ifaces')
119 return sorted (batman_ifaces
.split ())
123 def _get_batman_ifaces_ignore_regex (self
, ifaceobj
):
124 ifaces_ignore_regex
= ifaceobj
.get_attr_value_first ('batman-ifaces-ignore-regex')
125 if ifaces_ignore_regex
:
126 return re
.compile (r
"%s" % ifaces_ignore_regex
)
130 def _get_batman_attr (self
, ifaceobj
, attr
):
131 if attr
not in self
._batman
_attrs
:
132 raise ValueError ("_get_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr
)
134 value
= ifaceobj
.get_attr_value_first ('batman-%s' % attr
)
141 def _read_current_batman_attr (self
, ifaceobj
, attr
, dont_map
= False):
142 # 'routing_algo' needs special handling, D'oh.
144 attr_file_path
= "/sys/class/net/%s/mesh/%s" % (ifaceobj
.name
, attr
)
146 if attr
not in self
._batman
_attrs
:
147 raise ValueError ("_read_current_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr
)
149 attr_file_name
= self
._batman
_attrs
[attr
]['filename']
150 attr_file_path
= "/sys/class/net/%s/mesh/%s" % (ifaceobj
.name
, attr_file_name
)
153 return self
.read_file_oneline(attr_file_path
)
155 raise Exception ("_read_current_batman_attr (%s) %s" % (attr
, i
))
157 raise Exception ("_read_current_batman_attr: Integer value expected, got: %s" % value
)
160 def _set_batman_attr (self
, ifaceobj
, attr
, value
):
161 if attr
not in self
._batman
_attrs
:
162 raise ValueError ("_set_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr
)
164 attr_file_name
= self
._batman
_attrs
[attr
]['filename']
165 attr_file_path
= "/sys/class/net/%s/mesh/%s" % (ifaceobj
.name
, attr_file_name
)
167 self
.write_file(attr_file_path
, "%s\n" % value
)
169 raise Exception ("_set_batman_attr (%s): %s" % (attr
, i
))
172 def _batctl_if (self
, bat_iface
, mesh_iface
, op
):
173 if op
not in [ 'add', 'del' ]:
174 raise Exception ("_batctl_if() called with invalid \"op\" value: %s" % op
)
177 self
.logger
.debug ("Running batctl -m %s if %s %s" % (bat_iface
, op
, mesh_iface
))
178 utils
.exec_commandl(["batctl", "-m", bat_iface
, "if", op
, mesh_iface
])
179 except subprocess
.CalledProcessError
as c
:
180 raise Exception ("Command \"batctl -m %s if %s %s\" failed: %s" % (bat_iface
, op
, mesh_iface
, c
.output
))
181 except Exception as e
:
182 raise Exception ("_batctl_if: %s" % e
)
184 def _set_routing_algo (self
, routing_algo
):
185 if routing_algo
not in ['BATMAN_IV', 'BATMAN_V']:
186 raise Exception ("_set_routing_algo() called with invalid \"routing_algo\" value: %s" % routing_algo
)
189 self
.logger
.debug ("Running batctl ra %s" % routing_algo
)
190 batctl_output
= subprocess
.check_output (["batctl", "ra", routing_algo
], stderr
= subprocess
.STDOUT
)
191 except subprocess
.CalledProcessError
as c
:
192 raise Exception ("Command \"batctl ra %s\" failed: %s" % (routing_algo
, c
.output
))
193 except Exception as e
:
194 raise Exception ("_set_routing_algo: %s" % e
)
197 def _find_member_ifaces (self
, ifaceobj
, ignore
= True):
199 iface_ignore_re
= self
._get
_batman
_ifaces
_ignore
_regex
(ifaceobj
)
200 self
.logger
.info("batman: executing: %s" % " ".join(["batctl", "-m", ifaceobj
.name
, "if"]))
201 batctl_fh
= subprocess
.Popen (["batctl", "-m", ifaceobj
.name
, "if"], bufsize
= 4194304, stdout
= subprocess
.PIPE
).stdout
202 for line
in batctl_fh
.readlines ():
203 iface
= line
.split (':')[0]
204 if iface_ignore_re
and iface_ignore_re
.match (iface
) and ignore
:
207 members
.append (iface
)
209 return sorted (members
)
212 def get_dependent_ifacenames (self
, ifaceobj
, ifaceobjs_all
=None):
213 if not self
._is
_batman
_device
(ifaceobj
):
216 ifaceobj
.link_kind |
= ifaceLinkKind
.BATMAN_ADV
217 batman_ifaces
= self
._get
_batman
_ifaces
(ifaceobj
)
224 def _up (self
, ifaceobj
):
225 if self
._get
_batman
_ifaces
(ifaceobj
) == None:
226 raise Exception ('could not determine batman interfacaes')
228 # Verify existance of batman interfaces (should be present already)
230 for iface
in self
._get
_batman
_ifaces
(ifaceobj
):
231 if not self
.ipcmd
.link_exists (iface
):
232 self
.logger
.warn ('batman iface %s not present' % iface
)
235 batman_ifaces
.append (iface
)
237 if len (batman_ifaces
) == 0:
238 raise Exception ("None of the configured batman interfaces are available!")
240 routing_algo
= ifaceobj
.get_attr_value_first ('batman-routing-algo')
242 self
._set
_routing
_algo
(routing_algo
)
245 if_ignore_re
= self
._get
_batman
_ifaces
_ignore
_regex
(ifaceobj
)
246 # Is the batman main interface already present?
247 if self
.ipcmd
.link_exists (ifaceobj
.name
):
248 # Verify which member interfaces are present
249 members
= self
._find
_member
_ifaces
(ifaceobj
)
250 for iface
in members
:
251 if iface
not in batman_ifaces
:
252 self
._batctl
_if
(ifaceobj
.name
, iface
, 'del')
253 for iface
in batman_ifaces
:
254 if iface
not in members
:
255 self
._batctl
_if
(ifaceobj
.name
, iface
, 'add')
257 # Batman interfaces no present, add member interfaces to create it
259 for iface
in batman_ifaces
:
260 self
._batctl
_if
(ifaceobj
.name
, iface
, 'add')
262 # Check/set any B.A.T.M.A.N. adv. set within interface configuration
263 for attr
in self
._batman
_attrs
:
264 value_cfg
= self
._get
_batman
_attr
(ifaceobj
, attr
)
265 if value_cfg
and value_cfg
!= self
._read
_current
_batman
_attr
(ifaceobj
, attr
):
266 self
._set
_batman
_attr
(ifaceobj
, attr
, value_cfg
)
268 if ifaceobj
.addr_method
== 'manual':
269 netlink
.link_set_updown(ifaceobj
.name
, "up")
273 def _down (self
, ifaceobj
):
274 if not ifupdownflags
.flags
.PERFMODE
and not self
.ipcmd
.link_exists (ifaceobj
.name
):
277 members
= self
._find
_member
_ifaces
(ifaceobj
)
278 for iface
in members
:
279 self
._batctl
_if
(ifaceobj
.name
, iface
, 'del')
281 # The main interface will automagically vanish after the last member
282 # interface has been deleted.
285 def _query_check (self
, ifaceobj
, ifaceobjcurr
):
286 if not self
.ipcmd
.link_exists (ifaceobj
.name
):
289 batman_ifaces_cfg
= self
._get
_batman
_ifaces
(ifaceobj
)
290 batman_ifaces_real
= self
._find
_member
_ifaces
(ifaceobj
, False)
291 # Produce list of all current interfaces, tag interfaces ignored by
292 # regex with () around the iface name.
293 batman_ifaces_real_tagged
= []
294 iface_ignore_re_str
= ifaceobj
.get_attr_value_first ('batman-ifaces-ignore-regex')
295 iface_ignore_re
= self
._get
_batman
_ifaces
_ignore
_regex
(ifaceobj
)
297 # Assume everything's fine and wait for reality to prove us otherwise
300 # Interfaces configured but not active?
301 for iface
in batman_ifaces_cfg
:
302 if iface
not in batman_ifaces_real
:
305 # Interfaces active but not configured (or ignored)?
306 for iface
in batman_ifaces_real
:
307 if iface
not in batman_ifaces_cfg
:
308 if iface_ignore_re
and iface_ignore_re
.match (iface
):
309 batman_ifaces_real_tagged
.append ("(%s)" % iface
)
313 batman_ifaces_real_tagged
.append (iface
)
315 # Produce sorted list of active and ignored interfaces
316 ifaces_str
= " ".join (batman_ifaces_real_tagged
)
317 ifaceobjcurr
.update_config_with_status ('batman-ifaces', ifaces_str
, ifaces_ok
)
318 ifaceobjcurr
.update_config_with_status ('batman-ifaces-ignore-regex', iface_ignore_re_str
, 0)
320 # Check any B.A.T.M.A.N. adv. set within interface configuration
321 for attr
in self
._batman
_attrs
:
322 value_cfg
= self
._get
_batman
_attr
(ifaceobj
, attr
)
323 value_curr
= self
._read
_current
_batman
_attr
(ifaceobj
, attr
)
325 # Ignore this attribute if its'nt configured for this interface
330 if value_cfg
!= value_curr
:
333 ifaceobjcurr
.update_config_with_status ('batman-%s' % attr
, value_curr
, value_ok
)
335 routing_algo
= ifaceobj
.get_attr_value_first ('batman-routing-algo')
337 value_curr
= self
._read
_current
_batman
_attr
(ifaceobj
, "routing_algo", dont_map
= True)
340 if routing_algo
!= value_curr
:
343 ifaceobjcurr
.update_config_with_status ('batman-routing-algo', value_curr
, value_ok
)
346 def _query_running (self
, ifaceobjrunning
):
347 if not self
.ipcmd
.link_exists (ifaceobjrunning
.name
):
353 _run_ops
= {'pre-up' : _up
,
355 'query-checkcurr' : _query_check
}
356 # XXX 'query-running' : _query_running}
360 """ returns list of ops supported by this module """
361 return self
._run
_ops
.keys ()
364 def _init_command_handlers (self
):
366 self
.ipcmd
= LinkUtils()
369 def run (self
, ifaceobj
, operation
, query_ifaceobj
= None, **extra_args
):
370 """ run B.A.T.M.A.N. configuration on the interface object passed as argument
373 **ifaceobj** (object): iface object
375 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
378 **query_ifaceobj** (object): query check ifaceobject. This is only
379 valid when op is 'query-checkcurr'. It is an object same as
380 ifaceobj, but contains running attribute values and its config
381 status. The modules can use it to return queried running state
382 of interfaces. status is success if the running state is same
383 as user required state in ifaceobj. error otherwise.
385 op_handler
= self
._run
_ops
.get (operation
)
389 if (operation
!= 'query-running' and not self
._is
_batman
_device
(ifaceobj
)):
392 self
._init
_command
_handlers
()
394 if operation
== 'query-checkcurr':
395 op_handler (self
, ifaceobj
, query_ifaceobj
)
397 op_handler (self
, ifaceobj
)