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' ],
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')
93 for longname
, entry
in self
._modinfo
['attrs'].items ():
94 if entry
.get ('batman-attr', False) == False:
97 attr
= longname
.replace ("batman-", "")
98 self
._batman
_attrs
[attr
] = {
99 'filename' : attr
.replace ("-", "_"),
103 def _is_batman_device (self
, ifaceobj
):
104 if ifaceobj
.get_attr_value_first ('batman-ifaces'):
109 def _get_batman_ifaces (self
, ifaceobj
):
110 batman_ifaces
= ifaceobj
.get_attr_value_first ('batman-ifaces')
112 return sorted (batman_ifaces
.split ())
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
)
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
)
127 value
= ifaceobj
.get_attr_value_first ('batman-%s' % attr
)
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
)
138 attr_file_name
= self
._batman
_attrs
[attr
]['filename']
139 attr_file_path
= "/sys/class/net/%s/mesh/%s" % (ifaceobj
.name
, attr_file_name
)
141 return self
.read_file_oneline(attr_file_path
)
143 raise Exception ("_read_current_batman_attr (%s) %s" % (attr
, i
))
145 raise Exception ("_read_current_batman_attr: Integer value expected, got: %s" % value
)
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
)
152 attr_file_name
= self
._batman
_attrs
[attr
]['filename']
153 attr_file_path
= "/sys/class/net/%s/mesh/%s" % (ifaceobj
.name
, attr_file_name
)
155 self
.write_file(attr_file_path
, "%s\n" % value
)
157 raise Exception ("_set_batman_attr (%s): %s" % (attr
, i
))
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
)
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
)
173 def _find_member_ifaces (self
, ifaceobj
, ignore
= True):
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
:
183 members
.append (iface
)
185 return sorted (members
)
188 def get_dependent_ifacenames (self
, ifaceobj
, ifaceobjs_all
=None):
189 if not self
._is
_batman
_device
(ifaceobj
):
192 ifaceobj
.link_kind |
= ifaceLinkKind
.BATMAN_ADV
193 batman_ifaces
= self
._get
_batman
_ifaces
(ifaceobj
)
200 def _up (self
, ifaceobj
):
201 if self
._get
_batman
_ifaces
(ifaceobj
) == None:
202 raise Exception ('could not determine batman interfacaes')
204 # Verify existance of batman interfaces (should be present already)
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
)
211 batman_ifaces
.append (iface
)
213 if len (batman_ifaces
) == 0:
214 raise Exception ("None of the configured batman interfaces are available!")
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')
228 # Batman interfaces no present, add member interfaces to create it
230 for iface
in batman_ifaces
:
231 self
._batctl
_if
(ifaceobj
.name
, iface
, 'add')
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
)
239 if ifaceobj
.addr_method
== 'manual':
240 netlink
.link_set_updown(ifaceobj
.name
, "up")
244 def _down (self
, ifaceobj
):
245 if not ifupdownflags
.flags
.PERFMODE
and not self
.ipcmd
.link_exists (ifaceobj
.name
):
248 members
= self
._find
_member
_ifaces
(ifaceobj
)
249 for iface
in members
:
250 self
._batctl
_if
(ifaceobj
.name
, iface
, 'del')
252 # The main interface will automagically vanish after the last member
253 # interface has been deleted.
256 def _query_check (self
, ifaceobj
, ifaceobjcurr
):
257 if not self
.ipcmd
.link_exists (ifaceobj
.name
):
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
)
268 # Assume everything's fine and wait for reality to prove us otherwise
271 # Interfaces configured but not active?
272 for iface
in batman_ifaces_cfg
:
273 if iface
not in batman_ifaces_real
:
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
)
284 batman_ifaces_real_tagged
.append (iface
)
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)
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
)
296 # Ignore this attribute if its'nt configured for this interface
301 if value_cfg
!= value_curr
:
304 ifaceobjcurr
.update_config_with_status ('batman-%s' % attr
, value_curr
, value_ok
)
307 def _query_running (self
, ifaceobjrunning
):
308 if not self
.ipcmd
.link_exists (ifaceobjrunning
.name
):
314 _run_ops
= {'pre-up' : _up
,
316 'query-checkcurr' : _query_check
}
317 # XXX 'query-running' : _query_running}
321 """ returns list of ops supported by this module """
322 return self
._run
_ops
.keys ()
325 def _init_command_handlers (self
):
327 self
.ipcmd
= LinkUtils()
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
334 **ifaceobj** (object): iface object
336 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
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.
346 op_handler
= self
._run
_ops
.get (operation
)
350 if (operation
!= 'query-running' and not self
._is
_batman
_device
(ifaceobj
)):
353 self
._init
_command
_handlers
()
355 if operation
== 'query-checkcurr':
356 op_handler (self
, ifaceobj
, query_ifaceobj
)
358 op_handler (self
, ifaceobj
)