mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
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
This commit is contained in:
@@ -570,7 +570,7 @@ class ifupdownMain(ifupdownBase):
|
|||||||
self.logger.warning('error saving state (%s)' %str(e))
|
self.logger.warning('error saving state (%s)' %str(e))
|
||||||
|
|
||||||
def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
|
def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
|
||||||
excludepats=None, printdependency=None):
|
excludepats=None, printdependency=None, syntaxcheck=False):
|
||||||
""" up an interface """
|
""" up an interface """
|
||||||
|
|
||||||
if auto:
|
if auto:
|
||||||
@@ -582,6 +582,10 @@ class ifupdownMain(ifupdownBase):
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
# If only syntax check was requested, return here
|
||||||
|
if syntaxcheck:
|
||||||
|
return
|
||||||
|
|
||||||
if ifacenames:
|
if ifacenames:
|
||||||
# If iface list is given by the caller, always check if iface
|
# If iface list is given by the caller, always check if iface
|
||||||
# is present
|
# is present
|
||||||
|
@@ -27,7 +27,20 @@ class networkInterfaces():
|
|||||||
self.callbacks = {'iface_found' : None,
|
self.callbacks = {'iface_found' : None,
|
||||||
'validate' : None}
|
'validate' : None}
|
||||||
self.allow_classes = {}
|
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):
|
def subscribe(self, callback_name, callback_func):
|
||||||
if callback_name not in self.callbacks.keys():
|
if callback_name not in self.callbacks.keys():
|
||||||
@@ -69,13 +82,17 @@ class networkInterfaces():
|
|||||||
for f in glob.glob(sourced_file):
|
for f in glob.glob(sourced_file):
|
||||||
self.read_file(f)
|
self.read_file(f)
|
||||||
else:
|
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
|
return 0
|
||||||
|
|
||||||
def process_auto(self, lines, cur_idx, lineno):
|
def process_auto(self, lines, cur_idx, lineno):
|
||||||
for a in lines[cur_idx].split()[1:]:
|
auto_ifaces = lines[cur_idx].split()[1:]
|
||||||
self.auto_ifaces.append(a)
|
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
|
return 0
|
||||||
|
|
||||||
def _add_to_iface_config(self, iface_config, attrname, attrval):
|
def _add_to_iface_config(self, iface_config, attrname, attrval):
|
||||||
@@ -117,34 +134,28 @@ class networkInterfaces():
|
|||||||
iface_config = collections.OrderedDict()
|
iface_config = collections.OrderedDict()
|
||||||
for line_idx in range(cur_idx + 1, len(lines)):
|
for line_idx in range(cur_idx + 1, len(lines)):
|
||||||
l = lines[line_idx].strip('\n\t ')
|
l = lines[line_idx].strip('\n\t ')
|
||||||
|
|
||||||
if self.ignore_line(l) == 1:
|
if self.ignore_line(l) == 1:
|
||||||
continue
|
continue
|
||||||
|
if self._is_keyword(l.split()[0]):
|
||||||
if self.is_keyword(l.split()[0]) == True:
|
|
||||||
line_idx -= 1
|
line_idx -= 1
|
||||||
break
|
break
|
||||||
|
|
||||||
ifaceobj.raw_config.append(l)
|
ifaceobj.raw_config.append(l)
|
||||||
|
|
||||||
# preprocess vars (XXX: only preprocesses $IFACE for now)
|
# preprocess vars (XXX: only preprocesses $IFACE for now)
|
||||||
l = re.sub(r'\$IFACE', ifacename, l)
|
l = re.sub(r'\$IFACE', ifacename, l)
|
||||||
|
|
||||||
attrs = l.split(' ', 1)
|
attrs = l.split(' ', 1)
|
||||||
if len(attrs) < 2:
|
if len(attrs) < 2:
|
||||||
self.logger.warn('%s: invalid syntax at line %d'
|
self._parse_error(self._currentfile, line_idx,
|
||||||
%(ifacename, line_idx + 1))
|
'invalid syntax \'%s\'' %ifacename)
|
||||||
continue
|
continue
|
||||||
attrname = attrs[0]
|
attrname = attrs[0]
|
||||||
attrval = attrs[1].strip(' ')
|
attrval = attrs[1].strip(' ')
|
||||||
try:
|
try:
|
||||||
if not self.callbacks.get('validate')(attrname, attrval):
|
if not self.callbacks.get('validate')(attrname, attrval):
|
||||||
self.logger.warn('unsupported keyword (%s) at line %d'
|
self._parse_error(self._currentfile, line_idx + 1,
|
||||||
%(l, line_idx + 1))
|
'unsupported keyword (%s)' %l)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
self._add_to_iface_config(iface_config, attrname, attrval)
|
self._add_to_iface_config(iface_config, attrname, attrval)
|
||||||
|
|
||||||
lines_consumed = line_idx - cur_idx
|
lines_consumed = line_idx - cur_idx
|
||||||
|
|
||||||
# Create iface object
|
# Create iface object
|
||||||
@@ -168,7 +179,6 @@ class networkInterfaces():
|
|||||||
|
|
||||||
# Call iface found callback
|
# Call iface found callback
|
||||||
self.callbacks.get('iface_found')(ifaceobj)
|
self.callbacks.get('iface_found')(ifaceobj)
|
||||||
|
|
||||||
return lines_consumed # Return next index
|
return lines_consumed # Return next index
|
||||||
|
|
||||||
|
|
||||||
@@ -177,18 +187,15 @@ class networkInterfaces():
|
|||||||
'auto' : process_auto,
|
'auto' : process_auto,
|
||||||
'iface' : process_iface}
|
'iface' : process_iface}
|
||||||
|
|
||||||
def is_keyword(self, str):
|
def _is_keyword(self, str):
|
||||||
|
|
||||||
# The additional split here is for allow- keyword
|
# The additional split here is for allow- keyword
|
||||||
tmp_str = str.split('-')[0]
|
tmp_str = str.split('-')[0]
|
||||||
if tmp_str in self.network_elems.keys():
|
if tmp_str in self.network_elems.keys():
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def get_keyword_func(self, str):
|
def _get_keyword_func(self, str):
|
||||||
tmp_str = str.split('-')[0]
|
tmp_str = str.split('-')[0]
|
||||||
|
|
||||||
return self.network_elems.get(tmp_str)
|
return self.network_elems.get(tmp_str)
|
||||||
|
|
||||||
def get_allow_classes_for_iface(self, ifacename):
|
def get_allow_classes_for_iface(self, ifacename):
|
||||||
@@ -196,37 +203,27 @@ class networkInterfaces():
|
|||||||
for class_name, ifacenames in self.allow_classes.items():
|
for class_name, ifacenames in self.allow_classes.items():
|
||||||
if ifacename in ifacenames:
|
if ifacename in ifacenames:
|
||||||
classes.append(class_name)
|
classes.append(class_name)
|
||||||
|
|
||||||
return classes
|
return classes
|
||||||
|
|
||||||
def process_filedata(self, filedata):
|
def process_filedata(self, filedata):
|
||||||
lineno = 0
|
|
||||||
line_idx = 0
|
line_idx = 0
|
||||||
lines_consumed = 0
|
lines_consumed = 0
|
||||||
|
|
||||||
raw_config = filedata.split('\n')
|
raw_config = filedata.split('\n')
|
||||||
lines = [l.strip(' \n') for l in raw_config]
|
lines = [l.strip(' \n') for l in raw_config]
|
||||||
|
|
||||||
while (line_idx < len(lines)):
|
while (line_idx < len(lines)):
|
||||||
lineno = lineno + 1
|
|
||||||
|
|
||||||
if self.ignore_line(lines[line_idx]):
|
if self.ignore_line(lines[line_idx]):
|
||||||
line_idx += 1
|
line_idx += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
words = lines[line_idx].split()
|
words = lines[line_idx].split()
|
||||||
|
|
||||||
# Check if first element is a supported keyword
|
# Check if first element is a supported keyword
|
||||||
if self.is_keyword(words[0]):
|
if self._is_keyword(words[0]):
|
||||||
keyword_func = self.get_keyword_func(words[0])
|
keyword_func = self._get_keyword_func(words[0])
|
||||||
lines_consumed = keyword_func(self, lines, line_idx, lineno)
|
lines_consumed = keyword_func(self, lines, line_idx, line_idx)
|
||||||
line_idx += lines_consumed
|
line_idx += lines_consumed
|
||||||
else:
|
else:
|
||||||
self.logger.warning('could not process line %s' %l + ' at' +
|
self._parse_error(self._currentfile, line_idx + 1,
|
||||||
' lineno %d' %lineno)
|
'error processing line \'%s\'' %lines[line_idx])
|
||||||
|
|
||||||
line_idx += 1
|
line_idx += 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def run_template_engine(self, textdata):
|
def run_template_engine(self, textdata):
|
||||||
@@ -236,7 +233,6 @@ class networkInterfaces():
|
|||||||
self.logger.warning('template engine mako not found. ' +
|
self.logger.warning('template engine mako not found. ' +
|
||||||
'skip template parsing ..');
|
'skip template parsing ..');
|
||||||
return textdata
|
return textdata
|
||||||
|
|
||||||
t = Template(text=textdata, output_encoding='utf-8')
|
t = Template(text=textdata, output_encoding='utf-8')
|
||||||
return t.render()
|
return t.render()
|
||||||
|
|
||||||
@@ -244,18 +240,30 @@ class networkInterfaces():
|
|||||||
ifaces_file = filename
|
ifaces_file = filename
|
||||||
if not ifaces_file:
|
if not ifaces_file:
|
||||||
ifaces_file=self.ifaces_file
|
ifaces_file=self.ifaces_file
|
||||||
|
self._filestack.append(ifaces_file)
|
||||||
self.logger.debug('reading interfaces file %s' %ifaces_file)
|
self.logger.info('reading interfaces file %s' %ifaces_file)
|
||||||
f = open(ifaces_file)
|
f = open(ifaces_file)
|
||||||
filedata = f.read()
|
filedata = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# process line continuations
|
# process line continuations
|
||||||
filedata = ' '.join(d.strip() for d in filedata.split('\\'))
|
filedata = ' '.join(d.strip() for d in filedata.split('\\'))
|
||||||
|
|
||||||
# run through template engine
|
# run through template engine
|
||||||
filedata = self.run_template_engine(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.process_filedata(filedata)
|
||||||
|
self._filestack.pop()
|
||||||
|
|
||||||
def load(self, filename=None):
|
def load(self, filename=None):
|
||||||
return self.read_file(filename)
|
return self.read_file(filename)
|
||||||
|
@@ -228,8 +228,7 @@ class ifaceScheduler():
|
|||||||
if (ifupdownobj.ignore_error(str(e))):
|
if (ifupdownobj.ignore_error(str(e))):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise Exception('error running iface %s (%s)'
|
raise Exception('%s : (%s)' %(ifacename, str(e)))
|
||||||
%(ifacename, str(e)))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
|
def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
|
||||||
@@ -282,8 +281,7 @@ class ifaceScheduler():
|
|||||||
if (ifupdownobj.ignore_error(str(e))):
|
if (ifupdownobj.ignore_error(str(e))):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise Exception('error running iface %s (%s)'
|
raise Exception('%s : (%s)' %(ifacename, str(e)))
|
||||||
%(ifacename, str(e)))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
|
def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
|
||||||
|
@@ -35,12 +35,14 @@ def run_up(args):
|
|||||||
if args.noaddons:
|
if args.noaddons:
|
||||||
ifupdown_handle.up(['up'], args.all, args.CLASS, iflist,
|
ifupdown_handle.up(['up'], args.all, args.CLASS, iflist,
|
||||||
excludepats=args.excludepats,
|
excludepats=args.excludepats,
|
||||||
printdependency=args.printdependency)
|
printdependency=args.printdependency,
|
||||||
|
syntaxcheck=args.syntaxcheck)
|
||||||
else:
|
else:
|
||||||
ifupdown_handle.up(['pre-up', 'up', 'post-up'],
|
ifupdown_handle.up(['pre-up', 'up', 'post-up'],
|
||||||
args.all, args.CLASS, iflist,
|
args.all, args.CLASS, iflist,
|
||||||
excludepats=args.excludepats,
|
excludepats=args.excludepats,
|
||||||
printdependency=args.printdependency)
|
printdependency=args.printdependency,
|
||||||
|
syntaxcheck=args.syntaxcheck)
|
||||||
except:
|
except:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -198,6 +200,9 @@ def update_ifupdown_argparser(argparser):
|
|||||||
'up/down')
|
'up/down')
|
||||||
|
|
||||||
def update_ifup_argparser(argparser):
|
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)
|
update_ifupdown_argparser(argparser)
|
||||||
|
|
||||||
def update_ifdown_argparser(argparser):
|
def update_ifdown_argparser(argparser):
|
||||||
@@ -294,6 +299,21 @@ handlers = {'up' : run_up,
|
|||||||
'query' : run_query,
|
'query' : run_query,
|
||||||
'reload' : run_reload }
|
'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):
|
def main(argv):
|
||||||
""" main function """
|
""" main function """
|
||||||
args = None
|
args = None
|
||||||
@@ -313,13 +333,7 @@ def main(argv):
|
|||||||
exit(1)
|
exit(1)
|
||||||
# Command line arg parser
|
# Command line arg parser
|
||||||
args = parse_args(argv[1:], op)
|
args = parse_args(argv[1:], op)
|
||||||
if not args.iflist and not args.all:
|
if not validate_args(op, args):
|
||||||
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'
|
|
||||||
exit(1)
|
exit(1)
|
||||||
init(args)
|
init(args)
|
||||||
handlers.get(op)(args)
|
handlers.get(op)(args)
|
||||||
|
Reference in New Issue
Block a user