mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
Currently it is called from /usr/sbin/batctl what makes it impossible to use in case one runs a self compiled version for example
345 lines
13 KiB
Python
345 lines
13 KiB
Python
#!/usr/bin/python
|
|
#
|
|
# Copyright 2016-2017 Maximilian Wilhelm <max@sdn.clinic>
|
|
# Author: Maximilian Wilhelm, max@sdn.clinic
|
|
#
|
|
|
|
from ifupdown.iface import *
|
|
from ifupdownaddons.modulebase import moduleBase
|
|
from ifupdownaddons.iproute2 import iproute2
|
|
from ifupdown.netlink import netlink
|
|
import ifupdown.ifupdownflags as ifupdownflags
|
|
import logging
|
|
import re
|
|
import subprocess
|
|
|
|
|
|
class batman_adv (moduleBase):
|
|
""" ifupdown2 addon module to configure B.A.T.M.A.N. advanced interfaces """
|
|
|
|
_modinfo = {
|
|
'mhelp' : 'batman_adv module configures B.A.T.M.A.N. advanced interfaces.' +
|
|
'Every B.A.T.M.A.N. advanced interface needs at least on ethernet ' +
|
|
'interface to be creatable. You can specify a space separated list' +
|
|
'of interfaces by using the "batma-ifaces" paramater. If this parameter' +
|
|
'is set for an interfaces this module will do the magic.',
|
|
|
|
'attrs' : {
|
|
'batman-ifaces' : {
|
|
'help' : 'Interfaces to be part of this B.A.T.M.A.N. advanced instance',
|
|
'validvals' : [ '<interface-list>' ],
|
|
'required' : True,
|
|
},
|
|
|
|
'batman-ifaces-ignore-regex' : {
|
|
'help' : 'Interfaces to ignore when verifying configuration (regexp)',
|
|
'required' : False,
|
|
},
|
|
|
|
'batman-distributed-arp-table' : {
|
|
'help' : 'B.A.T.M.A.N. distributed ARP table',
|
|
'validvals' : [ 'enabled', 'disabled' ],
|
|
'required' : False,
|
|
'batman-attr' : True,
|
|
},
|
|
|
|
'batman-gw-mode' : {
|
|
'help' : 'B.A.T.M.A.N. gateway mode',
|
|
'validvals' : [ 'off', 'client', 'server' ],
|
|
'required' : False,
|
|
'example' : [ 'batman-gw-mode client' ],
|
|
'batman-attr' : True,
|
|
},
|
|
|
|
'batman-hop-penalty' : {
|
|
'help' : 'B.A.T.M.A.N. hop penalty',
|
|
'validvals' : [ '<number>' ],
|
|
'required' : False,
|
|
'batman-attr' : True,
|
|
},
|
|
|
|
'batman-multicast-mode' : {
|
|
'help' : 'B.A.T.M.A.N. multicast mode',
|
|
'validvals' : [ 'enabled', 'disabled' ],
|
|
'required' : False,
|
|
'batman-attr' : True,
|
|
},
|
|
}
|
|
}
|
|
|
|
_batman_attrs = {
|
|
}
|
|
|
|
|
|
def __init__ (self, *args, **kargs):
|
|
moduleBase.__init__ (self, *args, **kargs)
|
|
self.ipcmd = None
|
|
|
|
for longname, entry in self._modinfo['attrs'].items ():
|
|
if entry.get ('batman-attr', False) == False:
|
|
continue
|
|
|
|
attr = longname.replace ("batman-", "")
|
|
self._batman_attrs[attr] = {
|
|
'filename' : attr.replace ("-", "_"),
|
|
}
|
|
|
|
|
|
def _is_batman_device (self, ifaceobj):
|
|
if ifaceobj.get_attr_value_first ('batman-ifaces'):
|
|
return True
|
|
return False
|
|
|
|
|
|
def _get_batman_ifaces (self, ifaceobj ):
|
|
batman_ifaces = ifaceobj.get_attr_value_first ('batman-ifaces')
|
|
if batman_ifaces:
|
|
return sorted (batman_ifaces.split ())
|
|
return None
|
|
|
|
|
|
def _get_batman_ifaces_ignore_regex (self, ifaceobj):
|
|
ifaces_ignore_regex = ifaceobj.get_attr_value_first ('batman-ifaces-ignore-regex')
|
|
if ifaces_ignore_regex:
|
|
return re.compile (r"%s" % ifaces_ignore_regex)
|
|
return None
|
|
|
|
|
|
def _get_batman_attr (self, ifaceobj, attr):
|
|
if attr not in self._batman_attrs:
|
|
raise ValueError ("_get_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
|
|
|
|
value = ifaceobj.get_attr_value_first ('batman-%s' % attr)
|
|
if value:
|
|
return value
|
|
|
|
return None
|
|
|
|
|
|
def _read_current_batman_attr (self, ifaceobj, attr):
|
|
if attr not in self._batman_attrs:
|
|
raise ValueError ("_read_current_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
|
|
|
|
attr_file_name = self._batman_attrs[attr]['filename']
|
|
attr_file_path = "/sys/class/net/%s/mesh/%s" % (ifaceobj.name, attr_file_name)
|
|
try:
|
|
with open (attr_file_path, "r") as fh:
|
|
return fh.readline ().strip ()
|
|
except IOError as i:
|
|
raise Exception ("_read_current_batman_attr (%s) %s" % (attr, i))
|
|
except ValueError:
|
|
raise Exception ("_read_current_batman_attr: Integer value expected, got: %s" % value)
|
|
|
|
|
|
def _set_batman_attr (self, ifaceobj, attr, value):
|
|
if attr not in self._batman_attrs:
|
|
raise ValueError ("_set_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
|
|
|
|
attr_file_name = self._batman_attrs[attr]['filename']
|
|
attr_file_path = "/sys/class/net/%s/mesh/%s" % (ifaceobj.name, attr_file_name)
|
|
try:
|
|
with open (attr_file_path, "w") as fh:
|
|
fh.write ("%s\n" % value)
|
|
except IOError as i:
|
|
raise Exception ("_set_batman_attr (%s): %s" % (attr, i))
|
|
|
|
|
|
def _batctl_if (self, bat_iface, mesh_iface, op):
|
|
if op not in [ 'add', 'del' ]:
|
|
raise Exception ("_batctl_if() called with invalid \"op\" value: %s" % op)
|
|
|
|
try:
|
|
self.logger.debug ("Running batctl -m %s if %s %s" % (bat_iface, op, mesh_iface))
|
|
batctl_output = subprocess.check_output (["batctl", "-m", bat_iface, "if", op, mesh_iface], stderr = subprocess.STDOUT)
|
|
except subprocess.CalledProcessError as c:
|
|
raise Exception ("Command \"batctl -m %s if %s %s\" failed: %s" % (bat_iface, op, mesh_iface, c.output))
|
|
except Exception as e:
|
|
raise Exception ("_batctl_if: %s" % e)
|
|
|
|
|
|
def _find_member_ifaces (self, ifaceobj, ignore = True):
|
|
members = []
|
|
iface_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
|
|
batctl_fh = subprocess.Popen (["batctl", "-m", ifaceobj.name, "if"], bufsize = 4194304, stdout = subprocess.PIPE).stdout
|
|
for line in batctl_fh.readlines ():
|
|
iface = line.split (':')[0]
|
|
if iface_ignore_re and iface_ignore_re.match (iface) and ignore:
|
|
continue
|
|
|
|
members.append (iface)
|
|
|
|
return sorted (members)
|
|
|
|
|
|
def get_dependent_ifacenames (self, ifaceobj, ifaceobjs_all=None):
|
|
if not self._is_batman_device (ifaceobj):
|
|
return None
|
|
|
|
ifaceobj.link_kind |= ifaceLinkKind.BATMAN_ADV
|
|
batman_ifaces = self._get_batman_ifaces (ifaceobj)
|
|
if batman_ifaces:
|
|
return batman_ifaces
|
|
|
|
return [None]
|
|
|
|
|
|
def _up (self, ifaceobj):
|
|
if self._get_batman_ifaces (ifaceobj) == None:
|
|
raise Exception ('could not determine batman interfacaes')
|
|
|
|
# Verify existance of batman interfaces (should be present already)
|
|
batman_ifaces = []
|
|
for iface in self._get_batman_ifaces (ifaceobj):
|
|
if not self.ipcmd.link_exists (iface):
|
|
self.logger.warn ('batman iface %s not present' % iface)
|
|
continue
|
|
|
|
batman_ifaces.append (iface)
|
|
|
|
if len (batman_ifaces) == 0:
|
|
raise Exception ("None of the configured batman interfaces are available!")
|
|
|
|
if_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
|
|
# Is the batman main interface already present?
|
|
if self.ipcmd.link_exists (ifaceobj.name):
|
|
# Verify which member interfaces are present
|
|
members = self._find_member_ifaces (ifaceobj)
|
|
for iface in members:
|
|
if iface not in batman_ifaces:
|
|
self._batctl_if (ifaceobj.name, iface, 'del')
|
|
for iface in batman_ifaces:
|
|
if iface not in members:
|
|
self._batctl_if (ifaceobj.name, iface, 'add')
|
|
|
|
# Batman interfaces no present, add member interfaces to create it
|
|
else:
|
|
for iface in batman_ifaces:
|
|
self._batctl_if (ifaceobj.name, iface, 'add')
|
|
|
|
# Check/set any B.A.T.M.A.N. adv. set within interface configuration
|
|
for attr in self._batman_attrs:
|
|
value_cfg = self._get_batman_attr (ifaceobj, attr)
|
|
if value_cfg and value_cfg != self._read_current_batman_attr (ifaceobj, attr):
|
|
self._set_batman_attr (ifaceobj, attr, value_cfg)
|
|
|
|
if ifaceobj.addr_method == 'manual':
|
|
netlink.link_set_updown(ifaceobj.name, "up")
|
|
|
|
|
|
|
|
def _down (self, ifaceobj):
|
|
if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists (ifaceobj.name):
|
|
return
|
|
|
|
members = self._find_member_ifaces (ifaceobj)
|
|
for iface in members:
|
|
self._batctl_if (ifaceobj.name, iface, 'del')
|
|
|
|
# The main interface will automagically vanish after the last member
|
|
# interface has been deleted.
|
|
|
|
|
|
def _query_check (self, ifaceobj, ifaceobjcurr):
|
|
if not self.ipcmd.link_exists (ifaceobj.name):
|
|
return
|
|
|
|
batman_ifaces_cfg = self._get_batman_ifaces (ifaceobj)
|
|
batman_ifaces_real = self._find_member_ifaces (ifaceobj, False)
|
|
# Produce list of all current interfaces, tag interfaces ignored by
|
|
# regex with () around the iface name.
|
|
batman_ifaces_real_tagged = []
|
|
iface_ignore_re_str = ifaceobj.get_attr_value_first ('batman-ifaces-ignore-regex')
|
|
iface_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
|
|
|
|
# Assume everything's fine and wait for reality to prove us otherwise
|
|
ifaces_ok = 0
|
|
|
|
# Interfaces configured but not active?
|
|
for iface in batman_ifaces_cfg:
|
|
if iface not in batman_ifaces_real:
|
|
ifaces_ok = 1
|
|
|
|
# Interfaces active but not configured (or ignored)?
|
|
for iface in batman_ifaces_real:
|
|
if iface not in batman_ifaces_cfg:
|
|
if iface_ignore_re and iface_ignore_re.match (iface):
|
|
batman_ifaces_real_tagged.append ("(%s)" % iface)
|
|
continue
|
|
ifaces_ok = 1
|
|
else:
|
|
batman_ifaces_real_tagged.append (iface)
|
|
|
|
# Produce sorted list of active and ignored interfaces
|
|
ifaces_str = " ".join (batman_ifaces_real_tagged)
|
|
ifaceobjcurr.update_config_with_status ('batman-ifaces', ifaces_str, ifaces_ok)
|
|
ifaceobjcurr.update_config_with_status ('batman-ifaces-ignore-regex', iface_ignore_re_str, 0)
|
|
|
|
# Check any B.A.T.M.A.N. adv. set within interface configuration
|
|
for attr in self._batman_attrs:
|
|
value_cfg = self._get_batman_attr (ifaceobj, attr)
|
|
value_curr = self._read_current_batman_attr (ifaceobj, attr)
|
|
|
|
# Ignore this attribute if its'nt configured for this interface
|
|
if not value_cfg:
|
|
continue
|
|
|
|
value_ok = 0
|
|
if value_cfg != value_curr:
|
|
value_ok = 1
|
|
|
|
ifaceobjcurr.update_config_with_status ('batman-%s' % attr, value_curr, value_ok)
|
|
|
|
|
|
def _query_running (self, ifaceobjrunning):
|
|
if not self.ipcmd.link_exists (ifaceobjrunning.name):
|
|
return
|
|
|
|
# XXX Now what?
|
|
|
|
|
|
_run_ops = {'pre-up' : _up,
|
|
'post-down' : _down,
|
|
'query-checkcurr' : _query_check}
|
|
# XXX 'query-running' : _query_running}
|
|
|
|
|
|
def get_ops (self):
|
|
""" returns list of ops supported by this module """
|
|
return self._run_ops.keys ()
|
|
|
|
|
|
def _init_command_handlers (self):
|
|
if not self.ipcmd:
|
|
self.ipcmd = iproute2()
|
|
|
|
|
|
def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
|
|
""" run B.A.T.M.A.N. 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_batman_device (ifaceobj)):
|
|
return
|
|
|
|
self._init_command_handlers ()
|
|
|
|
if operation == 'query-checkcurr':
|
|
op_handler (self, ifaceobj, query_ifaceobj)
|
|
else:
|
|
op_handler (self, ifaceobj)
|