#!/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.bondutil 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.300 or swp[1-10].300' + ' or swp[1-10]sub[0-4].300') # explanations are shown below in each if clause regexs = [re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\]([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)"), re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"), re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")] if regexs[0].match(expr): # the first regex checks for exactly two levels of ranges defined only with square brackets # (e.g. swpxyz[10-23]subqwe[0-4].100) to handle naming with two levels of port names. m = regexs[0].match(expr) mlist = m.groups() if len(mlist) < 7: # we have problems and should not continue raise Exception('Error: unhandled glob expression %s\n%s' % (expr,errmsg)) prefix = mlist[0] suffix = mlist[6] start_index = int(mlist[1]) end_index = int(mlist[2]) sub_string = mlist[3] start_sub = int(mlist[4]) end_sub = int(mlist[5]) for i in range(start_index, end_index + 1): for j in range(start_sub, end_sub + 1): yield prefix + '%d%s%d' % (i,sub_string,j) + suffix elif regexs[1].match(expr) or regexs[2].match(expr): # the second regex for 1 level with a range (e.g. swp10-14.100 # the third regex checks for 1 level with [] (e.g. swp[10-14].100) start_index = 0 end_index = 0 if regexs[1].match(expr): m = regexs[1].match(expr) else: m = regexs[2].match(expr) 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]) for i in range(start_index, end_index + 1): yield prefix + '%d' %i + suffix else: # Could not match anything. self.logger.warn('%s' %(errmsg)) yield expr 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