From 9dce35612d2ce69b7797b948a98aed58e0b612bf Mon Sep 17 00:00:00 2001 From: roopa Date: Fri, 28 Mar 2014 06:03:14 -0700 Subject: [PATCH] warn on template rendering errors and continue + --syntax-check option to ifup + minor parser cleanups Ticket: CM-2488 Reviewed By: Testing Done: Tested ifupdown sanity and also the interfaces file in CM-2488 --- pkg/ifupdownmain.py | 6 ++- pkg/networkinterfaces.py | 94 ++++++++++++++++++++++------------------ pkg/scheduler.py | 6 +-- sbin/ifupdown | 32 ++++++++++---- 4 files changed, 81 insertions(+), 57 deletions(-) diff --git a/pkg/ifupdownmain.py b/pkg/ifupdownmain.py index 8a5ea6e..4140a19 100644 --- a/pkg/ifupdownmain.py +++ b/pkg/ifupdownmain.py @@ -570,7 +570,7 @@ class ifupdownMain(ifupdownBase): self.logger.warning('error saving state (%s)' %str(e)) def up(self, ops, auto=False, allow_classes=None, ifacenames=None, - excludepats=None, printdependency=None): + excludepats=None, printdependency=None, syntaxcheck=False): """ up an interface """ if auto: @@ -582,6 +582,10 @@ class ifupdownMain(ifupdownBase): except Exception: raise + # If only syntax check was requested, return here + if syntaxcheck: + return + if ifacenames: # If iface list is given by the caller, always check if iface # is present diff --git a/pkg/networkinterfaces.py b/pkg/networkinterfaces.py index 4b121b7..4992d6b 100644 --- a/pkg/networkinterfaces.py +++ b/pkg/networkinterfaces.py @@ -27,7 +27,20 @@ class networkInterfaces(): self.callbacks = {'iface_found' : None, 'validate' : None} self.allow_classes = {} + self._filestack = [self.ifaces_file] + @property + def _currentfile(self): + try: + return self._filestack[-1] + except: + return self.ifaces_file + + def _parse_error(self, filename, lineno, msg): + if lineno == -1: + self.logger.error('%s: %s' %(filename, msg)) + else: + self.logger.error('%s: line%d: %s' %(filename, lineno, msg)) def subscribe(self, callback_name, callback_func): if callback_name not in self.callbacks.keys(): @@ -69,13 +82,17 @@ class networkInterfaces(): for f in glob.glob(sourced_file): self.read_file(f) else: - self.logger.warn('unable to read source line at %d', lineno) - + self._parse_error(self._currentfile, lineno, + 'unable to read source line') return 0 def process_auto(self, lines, cur_idx, lineno): - for a in lines[cur_idx].split()[1:]: - self.auto_ifaces.append(a) + auto_ifaces = lines[cur_idx].split()[1:] + if not auto_ifaces: + self._parse_error(self._currentfile, lineno + 1, + 'invalid auto line \'%s\''%lines[cur_idx]) + return 0 + [self.auto_ifaces.append(a) for a in auto_ifaces] return 0 def _add_to_iface_config(self, iface_config, attrname, attrval): @@ -117,34 +134,28 @@ class networkInterfaces(): iface_config = collections.OrderedDict() for line_idx in range(cur_idx + 1, len(lines)): l = lines[line_idx].strip('\n\t ') - if self.ignore_line(l) == 1: continue - - if self.is_keyword(l.split()[0]) == True: + if self._is_keyword(l.split()[0]): line_idx -= 1 break - ifaceobj.raw_config.append(l) - # preprocess vars (XXX: only preprocesses $IFACE for now) l = re.sub(r'\$IFACE', ifacename, l) - attrs = l.split(' ', 1) if len(attrs) < 2: - self.logger.warn('%s: invalid syntax at line %d' - %(ifacename, line_idx + 1)) + self._parse_error(self._currentfile, line_idx, + 'invalid syntax \'%s\'' %ifacename) continue attrname = attrs[0] attrval = attrs[1].strip(' ') try: if not self.callbacks.get('validate')(attrname, attrval): - self.logger.warn('unsupported keyword (%s) at line %d' - %(l, line_idx + 1)) + self._parse_error(self._currentfile, line_idx + 1, + 'unsupported keyword (%s)' %l) except: pass self._add_to_iface_config(iface_config, attrname, attrval) - lines_consumed = line_idx - cur_idx # Create iface object @@ -168,7 +179,6 @@ class networkInterfaces(): # Call iface found callback self.callbacks.get('iface_found')(ifaceobj) - return lines_consumed # Return next index @@ -177,18 +187,15 @@ class networkInterfaces(): 'auto' : process_auto, 'iface' : process_iface} - def is_keyword(self, str): - + def _is_keyword(self, str): # The additional split here is for allow- keyword tmp_str = str.split('-')[0] if tmp_str in self.network_elems.keys(): return 1 - return 0 - def get_keyword_func(self, str): + def _get_keyword_func(self, str): tmp_str = str.split('-')[0] - return self.network_elems.get(tmp_str) def get_allow_classes_for_iface(self, ifacename): @@ -196,37 +203,27 @@ class networkInterfaces(): for class_name, ifacenames in self.allow_classes.items(): if ifacename in ifacenames: classes.append(class_name) - return classes def process_filedata(self, filedata): - lineno = 0 line_idx = 0 lines_consumed = 0 - raw_config = filedata.split('\n') lines = [l.strip(' \n') for l in raw_config] - while (line_idx < len(lines)): - lineno = lineno + 1 - if self.ignore_line(lines[line_idx]): line_idx += 1 continue - words = lines[line_idx].split() - # Check if first element is a supported keyword - if self.is_keyword(words[0]): - keyword_func = self.get_keyword_func(words[0]) - lines_consumed = keyword_func(self, lines, line_idx, lineno) + if self._is_keyword(words[0]): + keyword_func = self._get_keyword_func(words[0]) + lines_consumed = keyword_func(self, lines, line_idx, line_idx) line_idx += lines_consumed else: - self.logger.warning('could not process line %s' %l + ' at' + - ' lineno %d' %lineno) - + self._parse_error(self._currentfile, line_idx + 1, + 'error processing line \'%s\'' %lines[line_idx]) line_idx += 1 - return 0 def run_template_engine(self, textdata): @@ -236,7 +233,6 @@ class networkInterfaces(): self.logger.warning('template engine mako not found. ' + 'skip template parsing ..'); return textdata - t = Template(text=textdata, output_encoding='utf-8') return t.render() @@ -244,18 +240,30 @@ class networkInterfaces(): ifaces_file = filename if not ifaces_file: ifaces_file=self.ifaces_file - - self.logger.debug('reading interfaces file %s' %ifaces_file) + self._filestack.append(ifaces_file) + self.logger.info('reading interfaces file %s' %ifaces_file) f = open(ifaces_file) filedata = f.read() f.close() - # process line continuations filedata = ' '.join(d.strip() for d in filedata.split('\\')) - # run through template engine - filedata = self.run_template_engine(filedata) - self.process_filedata(filedata) + try: + self.logger.info('template processing on interfaces file %s ...' + %ifaces_file) + rendered_filedata = self.run_template_engine(filedata) + except Exception, e: + self._parse_error(self._currentfile, -1, + 'failed to render template (%s).' %str(e) + + 'Continue without template rendering ...') + rendered_filedata = None + pass + self.logger.info('parsing interfaces file %s ...' %ifaces_file) + if rendered_filedata: + self.process_filedata(rendered_filedata) + else: + self.process_filedata(filedata) + self._filestack.pop() def load(self, filename=None): return self.read_file(filename) diff --git a/pkg/scheduler.py b/pkg/scheduler.py index 0767435..a6cfe6a 100644 --- a/pkg/scheduler.py +++ b/pkg/scheduler.py @@ -228,8 +228,7 @@ class ifaceScheduler(): if (ifupdownobj.ignore_error(str(e))): pass else: - raise Exception('error running iface %s (%s)' - %(ifacename, str(e))) + raise Exception('%s : (%s)' %(ifacename, str(e))) @classmethod def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None, @@ -282,8 +281,7 @@ class ifaceScheduler(): if (ifupdownobj.ignore_error(str(e))): pass else: - raise Exception('error running iface %s (%s)' - %(ifacename, str(e))) + raise Exception('%s : (%s)' %(ifacename, str(e))) @classmethod def sched_ifaces(cls, ifupdownobj, ifacenames, ops, diff --git a/sbin/ifupdown b/sbin/ifupdown index b589307..96982c1 100755 --- a/sbin/ifupdown +++ b/sbin/ifupdown @@ -35,12 +35,14 @@ def run_up(args): if args.noaddons: ifupdown_handle.up(['up'], args.all, args.CLASS, iflist, excludepats=args.excludepats, - printdependency=args.printdependency) + printdependency=args.printdependency, + syntaxcheck=args.syntaxcheck) else: ifupdown_handle.up(['pre-up', 'up', 'post-up'], args.all, args.CLASS, iflist, excludepats=args.excludepats, - printdependency=args.printdependency) + printdependency=args.printdependency, + syntaxcheck=args.syntaxcheck) except: raise @@ -198,6 +200,9 @@ def update_ifupdown_argparser(argparser): 'up/down') def update_ifup_argparser(argparser): + argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck', + action='store_true', + help='Only run the interfaces file parser') update_ifupdown_argparser(argparser) def update_ifdown_argparser(argparser): @@ -294,6 +299,21 @@ handlers = {'up' : run_up, 'query' : run_query, 'reload' : run_reload } +def validate_args(op, args): + if op == 'up' and args.syntaxcheck: + if args.iflist or args.all: + print 'ignoring interface options ..' + return True + if op == 'query' and args.syntaxhelp: + return True + if not args.iflist and not args.all: + print '\'-a\' option or interface list are required' + return False + if args.iflist and args.all: + print '\'-a\' option and interface list are mutually exclusive' + return False + return True + def main(argv): """ main function """ args = None @@ -313,13 +333,7 @@ def main(argv): exit(1) # Command line arg parser args = parse_args(argv[1:], op) - if not args.iflist and not args.all: - if op != 'query' or not args.syntaxhelp: - print '\'-a\' option or interface list are required' - exit(1) - - if args.iflist and args.all: - print '\'-a\' option and interface list are mutually exclusive' + if not validate_args(op, args): exit(1) init(args) handlers.get(op)(args) -- 2.39.5