mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
Merge 'vlan filtering bridge + vxlan + mlag + vrr' support from internal
tree to external This also combines python-ifupdown2 and python-ifupdown2-addons package into a single python-ifupdown2 package
This commit is contained in:
363
ifupdown2/ifupdownaddons/modulebase.py
Normal file
363
ifupdown2/ifupdownaddons/modulebase.py
Normal file
@@ -0,0 +1,363 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
|
||||
import os
|
||||
import re
|
||||
import io
|
||||
import logging
|
||||
import subprocess
|
||||
import traceback
|
||||
from ifupdown.iface import *
|
||||
#from ifupdownaddons.iproute2 import *
|
||||
#from ifupdownaddons.dhclient import *
|
||||
#from ifupdownaddons.bridgeutils import *
|
||||
#from ifupdownaddons.mstpctlutil import *
|
||||
#from ifupdownaddons.ifenslaveutil import *
|
||||
|
||||
class moduleBase(object):
|
||||
""" Base class for ifupdown addon modules
|
||||
|
||||
Provides common infrastructure methods for all addon modules """
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
modulename = self.__class__.__name__
|
||||
self.logger = logging.getLogger('ifupdown.' + modulename)
|
||||
self.FORCE = kargs.get('force', False)
|
||||
"""force interface configuration"""
|
||||
self.DRYRUN = kargs.get('dryrun', False)
|
||||
"""only predend you are applying configuration, dont really do it"""
|
||||
self.NOWAIT = kargs.get('nowait', False)
|
||||
self.PERFMODE = kargs.get('perfmode', False)
|
||||
self.CACHE = kargs.get('cache', False)
|
||||
self.CACHE_FLAGS = kargs.get('cacheflags', 0x0)
|
||||
|
||||
def log_warn(self, str):
|
||||
""" log a warning if err str is not one of which we should ignore """
|
||||
if not self.ignore_error(str):
|
||||
if self.logger.getEffectiveLevel() == logging.DEBUG:
|
||||
traceback.print_stack()
|
||||
self.logger.warn(str)
|
||||
pass
|
||||
|
||||
def log_error(self, str):
|
||||
""" log an err if err str is not one of which we should ignore and raise an exception """
|
||||
if not self.ignore_error(str):
|
||||
if self.logger.getEffectiveLevel() == logging.DEBUG:
|
||||
traceback.print_stack()
|
||||
raise Exception(str)
|
||||
else:
|
||||
pass
|
||||
|
||||
def is_process_running(self, procName):
|
||||
try:
|
||||
self.exec_command('/bin/pidof -x %s' % procName)
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def exec_command(self, cmd, cmdenv=None):
|
||||
""" execute command passed as argument.
|
||||
|
||||
Args:
|
||||
cmd (str): command to execute
|
||||
|
||||
Kwargs:
|
||||
cmdenv (dict): environment variable name value pairs
|
||||
"""
|
||||
cmd_returncode = 0
|
||||
cmdout = ''
|
||||
|
||||
try:
|
||||
self.logger.info('Executing ' + cmd)
|
||||
if self.DRYRUN:
|
||||
return cmdout
|
||||
ch = subprocess.Popen(cmd.split(),
|
||||
stdout=subprocess.PIPE,
|
||||
shell=False, env=cmdenv,
|
||||
stderr=subprocess.STDOUT,
|
||||
close_fds=True)
|
||||
cmdout = ch.communicate()[0]
|
||||
cmd_returncode = ch.wait()
|
||||
except OSError, e:
|
||||
raise Exception('could not execute ' + cmd +
|
||||
'(' + str(e) + ')')
|
||||
if cmd_returncode != 0:
|
||||
raise Exception('error executing cmd \'%s\'' %cmd +
|
||||
'(' + cmdout.strip('\n ') + ')')
|
||||
return cmdout
|
||||
|
||||
def exec_command_talk_stdin(self, cmd, stdinbuf):
|
||||
""" execute command passed as argument and write contents of stdinbuf
|
||||
into stdin of the cmd
|
||||
|
||||
Args:
|
||||
cmd (str): command to execute
|
||||
stdinbuf (str): string to write to stdin of the cmd process
|
||||
"""
|
||||
cmd_returncode = 0
|
||||
cmdout = ''
|
||||
|
||||
try:
|
||||
self.logger.info('Executing %s (stdin=%s)' %(cmd, stdinbuf))
|
||||
if self.DRYRUN:
|
||||
return cmdout
|
||||
ch = subprocess.Popen(cmd.split(),
|
||||
stdout=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE,
|
||||
shell=False, env=cmdenv,
|
||||
stderr=subprocess.STDOUT,
|
||||
close_fds=True)
|
||||
cmdout = ch.communicate(input=stdinbuf)[0]
|
||||
cmd_returncode = ch.wait()
|
||||
except OSError, e:
|
||||
raise Exception('could not execute ' + cmd +
|
||||
'(' + str(e) + ')')
|
||||
if cmd_returncode != 0:
|
||||
raise Exception('error executing cmd \'%s (%s)\''
|
||||
%(cmd, stdinbuf) + '(' + cmdout.strip('\n ') + ')')
|
||||
return cmdout
|
||||
|
||||
def get_ifaces_from_proc(self):
|
||||
ifacenames = []
|
||||
with open('/proc/net/dev') as f:
|
||||
try:
|
||||
lines = f.readlines()
|
||||
for line in lines:
|
||||
ifacenames.append(line.split()[0].strip(': '))
|
||||
except:
|
||||
raise
|
||||
return ifacenames
|
||||
|
||||
def parse_regex(self, expr, ifacenames=None):
|
||||
try:
|
||||
proc_ifacenames = self.get_ifaces_from_proc()
|
||||
except:
|
||||
self.logger.warn('error reading ifaces from proc')
|
||||
for proc_ifacename in proc_ifacenames:
|
||||
if re.search(expr + '$', proc_ifacename):
|
||||
yield proc_ifacename
|
||||
if not ifacenames:
|
||||
return
|
||||
for ifacename in ifacenames:
|
||||
if re.search(expr + '$', ifacename):
|
||||
yield ifacename
|
||||
|
||||
def parse_glob(self, expr):
|
||||
errmsg = ('error parsing glob expression \'%s\'' %expr +
|
||||
' (supported glob syntax: swp1-10 or swp[1-10])')
|
||||
start_index = 0
|
||||
end_index = 0
|
||||
try:
|
||||
regexs = [re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
|
||||
re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
|
||||
for r in regexs:
|
||||
m = r.match(expr)
|
||||
if not m:
|
||||
continue
|
||||
mlist = m.groups()
|
||||
if len(mlist) != 4:
|
||||
raise Exception(errmsg + '(unexpected len)')
|
||||
prefix = mlist[0]
|
||||
suffix = mlist[3]
|
||||
start_index = int(mlist[1])
|
||||
end_index = int(mlist[2])
|
||||
except:
|
||||
self.logger.warn(errmsg)
|
||||
pass
|
||||
if not start_index and not end_index:
|
||||
self.logger.warn(errmsg)
|
||||
yield expr
|
||||
else:
|
||||
for i in range(start_index, end_index + 1):
|
||||
yield prefix + '%d' %i + suffix
|
||||
|
||||
def parse_port_list(self, port_expr, ifacenames=None):
|
||||
""" parse port list containing glob and regex
|
||||
|
||||
Args:
|
||||
port_expr (str): expression
|
||||
ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
|
||||
"""
|
||||
regex = 0
|
||||
glob = 0
|
||||
portlist = []
|
||||
|
||||
if not port_expr:
|
||||
return None
|
||||
for expr in re.split(r'[\s\t]\s*', port_expr):
|
||||
if expr == 'noregex':
|
||||
regex = 0
|
||||
elif expr == 'noglob':
|
||||
glob = 0
|
||||
elif expr == 'regex':
|
||||
regex = 1
|
||||
elif expr == 'glob':
|
||||
glob = 1
|
||||
elif regex:
|
||||
for port in self.parse_regex(expr, ifacenames):
|
||||
if port not in portlist:
|
||||
portlist.append(port)
|
||||
regex = 0
|
||||
elif glob:
|
||||
for port in self.parse_glob(expr):
|
||||
portlist.append(port)
|
||||
glob = 0
|
||||
else:
|
||||
portlist.append(expr)
|
||||
if not portlist:
|
||||
return None
|
||||
return portlist
|
||||
|
||||
def ignore_error(self, errmsg):
|
||||
if (self.FORCE or re.search(r'exists', errmsg,
|
||||
re.IGNORECASE | re.MULTILINE)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def write_file(self, filename, strexpr):
|
||||
""" writes string to a file """
|
||||
try:
|
||||
self.logger.info('writing \'%s\'' %strexpr +
|
||||
' to file %s' %filename)
|
||||
if self.DRYRUN:
|
||||
return 0
|
||||
with open(filename, 'w') as f:
|
||||
f.write(strexpr)
|
||||
except IOError, e:
|
||||
self.logger.warn('error writing to file %s'
|
||||
%filename + '(' + str(e) + ')')
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def read_file(self, filename):
|
||||
""" read file and return lines from the file """
|
||||
try:
|
||||
self.logger.info('reading \'%s\'' %filename)
|
||||
with open(filename, 'r') as f:
|
||||
return f.readlines()
|
||||
except:
|
||||
return None
|
||||
return None
|
||||
|
||||
def read_file_oneline(self, filename):
|
||||
""" reads and returns first line from the file """
|
||||
try:
|
||||
self.logger.info('reading \'%s\'' %filename)
|
||||
with open(filename, 'r') as f:
|
||||
return f.readline().strip('\n')
|
||||
except:
|
||||
return None
|
||||
return None
|
||||
|
||||
def sysctl_set(self, variable, value):
|
||||
""" set sysctl variable to value passed as argument """
|
||||
self.exec_command('sysctl %s=' %variable + '%s' %value)
|
||||
|
||||
def sysctl_get(self, variable):
|
||||
""" get value of sysctl variable """
|
||||
return self.exec_command('sysctl %s' %variable).split('=')[1].strip()
|
||||
|
||||
def set_iface_attr(self, ifaceobj, attr_name, attr_valsetfunc,
|
||||
prehook=None, prehookargs=None):
|
||||
ifacename = ifaceobj.name
|
||||
attrvalue = ifaceobj.get_attr_value_first(attr_name)
|
||||
if attrvalue:
|
||||
if prehook:
|
||||
if prehookargs:
|
||||
prehook(prehookargs)
|
||||
else:
|
||||
prehook(ifacename)
|
||||
attr_valsetfunc(ifacename, attrvalue)
|
||||
|
||||
def query_n_update_ifaceobjcurr_attr(self, ifaceobj, ifaceobjcurr,
|
||||
attr_name, attr_valgetfunc,
|
||||
attr_valgetextraarg=None):
|
||||
attrvalue = ifaceobj.get_attr_value_first(attr_name)
|
||||
if not attrvalue:
|
||||
return
|
||||
if attr_valgetextraarg:
|
||||
runningattrvalue = attr_valgetfunc(ifaceobj.name,
|
||||
attr_valgetextraarg)
|
||||
else:
|
||||
runningattrvalue = attr_valgetfunc(ifaceobj.name)
|
||||
if (not runningattrvalue or
|
||||
(runningattrvalue != attrvalue)):
|
||||
ifaceobjcurr.update_config_with_status(attr_name,
|
||||
runningattrvalue, 1)
|
||||
else:
|
||||
ifaceobjcurr.update_config_with_status(attr_name,
|
||||
runningattrvalue, 0)
|
||||
|
||||
def dict_key_subset(self, a, b):
|
||||
""" returns a list of differing keys """
|
||||
return [x for x in a if x in b]
|
||||
|
||||
def get_mod_attrs(self):
|
||||
""" returns list of all module attrs defined in the module _modinfo dict"""
|
||||
try:
|
||||
return self._modinfo.get('attrs').keys()
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_mod_attr(self, attrname):
|
||||
""" returns module attr info """
|
||||
try:
|
||||
return self._modinfo.get('attrs', {}).get(attrname)
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_mod_subattr(self, attrname, subattrname):
|
||||
""" returns module attrs defined in the module _modinfo dict"""
|
||||
try:
|
||||
return reduce(lambda d, k: d[k], ['attrs', attrname, subattrname],
|
||||
self._modinfo)
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_modinfo(self):
|
||||
""" return module info """
|
||||
try:
|
||||
return self._modinfo
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_flags(self):
|
||||
return dict(force=self.FORCE, dryrun=self.DRYRUN, nowait=self.NOWAIT,
|
||||
perfmode=self.PERFMODE, cache=self.CACHE,
|
||||
cacheflags=self.CACHE_FLAGS)
|
||||
|
||||
def _get_reserved_vlan_range(self):
|
||||
start = end = 0
|
||||
get_resvvlan = '/usr/share/python-ifupdown2/get_reserved_vlan_range.sh'
|
||||
if not os.path.exists(get_resvvlan):
|
||||
return (start, end)
|
||||
try:
|
||||
(s, e) = self.exec_command(get_resvvlan).strip('\n').split('-')
|
||||
start = int(s)
|
||||
end = int(e)
|
||||
except Exception, e:
|
||||
self.logger.debug('%s failed (%s)' %(get_resvvlan, str(e)))
|
||||
# ignore errors
|
||||
pass
|
||||
return (start, end)
|
||||
|
||||
def _handle_reserved_vlan(self, vlanid, logprefix=''):
|
||||
""" Helper function to check and warn if the vlanid falls in the
|
||||
reserved vlan range """
|
||||
if vlanid in range(self._resv_vlan_range[0],
|
||||
self._resv_vlan_range[1]):
|
||||
self.logger.error('%s: reserved vlan %d being used'
|
||||
%(logprefix, vlanid) + ' (reserved vlan range %d-%d)'
|
||||
%(self._resv_vlan_range[0], self._resv_vlan_range[1]))
|
||||
return True
|
||||
return False
|
||||
|
||||
def _valid_ethaddr(self, ethaddr):
|
||||
""" Check if address is 00:00:00:00:00:00 """
|
||||
if not ethaddr or re.match('00:00:00:00:00:00', ethaddr):
|
||||
return False
|
||||
return True
|
Reference in New Issue
Block a user