1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00

Fixes to some corner cases + support for some missing 'options and

attributes' for backward compatibility

Ticket: CM-1438
Reviewed By:
Testing Done: Tested ifupdown sanity and new functionality

support for:
- -i <interface file>
- template lookup path and move all template handling to a separate
  module template.py
- new ifupdown2 config file /etc/network/ifupdown2/ifupdown2.conf
- bridge_waitport and bridge_maxwait
- moved addons.conf to /var/lib/ifupdownaddons/
This commit is contained in:
roopa
2014-04-18 14:09:20 -07:00
parent 03d5166bff
commit 14dc390d71
9 changed files with 198 additions and 93 deletions

8
TODO
View File

@@ -40,3 +40,11 @@ TODO:
PATH the command search path: /usr/local/sbin:/usr/local/bin:
/usr/sbin:/usr/bin:/sbin:/bin
- ifquery -r shows bridge-pathcosts
auto bridge100
iface bridge100
bridge-ports swp13.100 swp14.100
bridge-pathcosts swp13.100=2 swp14.100=2
- Does not purge old addresses

7
config/ifupdown2.conf Normal file
View File

@@ -0,0 +1,7 @@
#
#
#
TEMPLATE_ENGINE=mako
TEMPLATE_LOOKUPPATH=/etc/network/ifupdown2/templates

View File

@@ -383,6 +383,7 @@ class iface():
for cname, cvaluelist in config.items():
idx = 0
for cv in cvaluelist:
if not cv: continue
if with_status:
outbuf += indent + '%s %s %s\n' %(cname, cv,
self.get_config_attr_status_str(cname, idx))

View File

@@ -40,7 +40,7 @@ class ifupdownMain(ifupdownBase):
scripts_dir='/etc/network'
addon_modules_dir='/usr/share/ifupdownaddons'
addon_modules_configfile='/etc/network/.addons.conf'
addon_modules_configfile='/var/lib/ifupdownaddons/addons.conf'
# iface dictionary in the below format:
# { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
@@ -106,9 +106,11 @@ class ifupdownMain(ifupdownBase):
# ifupdown object interface scheduler pre and posthooks
sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
def __init__(self, force=False, dryrun=False, nowait=False,
def __init__(self, config={},
force=False, dryrun=False, nowait=False,
perfmode=False, withdepends=False, njobs=1,
cache=False, addons_enable=True, statemanager_enable=True):
cache=False, addons_enable=True, statemanager_enable=True,
interfacesfile='/etc/network/interfaces'):
self.logger = logging.getLogger('ifupdown')
self.FORCE = force
self.DRYRUN = dryrun
@@ -117,6 +119,9 @@ class ifupdownMain(ifupdownBase):
self.WITH_DEPENDS = withdepends
self.STATEMANAGER_ENABLE = statemanager_enable
self.CACHE = cache
self.interfacesfile = interfacesfile
self.config = config
self.logger.debug(self.config)
# Can be used to provide hints for caching
self.CACHE_FLAGS = 0x0
@@ -295,8 +300,6 @@ class ifupdownMain(ifupdownBase):
dlist = None
pass
if dlist:
self.logger.debug('%s: ' %ifaceobj.name +
'lowerifaces/dependents: %s' %str(dlist))
break
return dlist
@@ -323,7 +326,7 @@ class ifupdownMain(ifupdownBase):
if dlist:
self.preprocess_dependency_list(ifaceobj.name,
dlist, ops)
self.logger.debug('%s: lowerifaces/dependents after processing: %s'
self.logger.debug('%s: lowerifaces/dependents: %s'
%(i, str(dlist)))
ifaceobj.lowerifaces = dlist
[iqueue.append(d) for d in dlist]
@@ -352,18 +355,18 @@ class ifupdownMain(ifupdownBase):
pass
return False
def read_default_iface_config(self):
def read_iface_config(self):
""" Reads default network interface config /etc/network/interfaces. """
nifaces = networkInterfaces()
nifaces = networkInterfaces(self.interfacesfile,
template_engine=self.config.get('template_engine'),
template_lookuppath=self.config.get('template_lookuppath'))
nifaces.subscribe('iface_found', self._save_iface)
nifaces.subscribe('validate', self._module_syntax_checker)
nifaces.load()
def read_iface_config(self):
return self.read_default_iface_config()
def read_old_iface_config(self):
""" Reads the saved iface config instead of default iface config. """
""" Reads the saved iface config instead of default iface config.
And saved iface config is already read by the statemanager """
self.ifaceobjdict = copy.deepcopy(self.statemanager.ifaceobjdict)
def _load_addon_modules_config(self):
@@ -420,6 +423,7 @@ class ifupdownMain(ifupdownBase):
self.module_ops['query'] = self.modules.keys()
self.module_ops['query-raw'] = self.modules.keys()
def _modules_help(self):
""" Prints addon modules supported syntax """
@@ -668,12 +672,11 @@ class ifupdownMain(ifupdownBase):
excludepats, i)]
if not filtered_ifacenames:
raise Exception('no ifaces found matching given allow lists')
self.populate_dependency_info(ops, filtered_ifacenames)
if printdependency:
self.populate_dependency_info(ops, filtered_ifacenames)
self.print_dependency(filtered_ifacenames, printdependency)
return
else:
self.populate_dependency_info(ops)
try:
self._sched_ifaces(filtered_ifacenames, ops)
@@ -727,12 +730,10 @@ class ifupdownMain(ifupdownBase):
raise Exception('no ifaces found matching ' +
'given allow lists')
self.populate_dependency_info(ops, filtered_ifacenames)
if ops[0] == 'query-dependency' and printdependency:
self.populate_dependency_info(ops, filtered_ifacenames)
self.print_dependency(filtered_ifacenames, printdependency)
return
else:
self.populate_dependency_info(ops)
if ops[0] == 'query':
return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
@@ -751,7 +752,7 @@ class ifupdownMain(ifupdownBase):
return
def reload(self, upops, downops, auto=False, allow=None,
ifacenames=None, excludepats=None):
ifacenames=None, excludepats=None, usecurrentconfig=False):
""" reload interface config """
allow_classes = []
@@ -762,23 +763,21 @@ class ifupdownMain(ifupdownBase):
self.WITH_DEPENDS = True
try:
# Read the current interface config
self.read_iface_config()
except:
raise
# generate dependency graph of interfaces
self.populate_dependency_info(upops)
if (not usecurrentconfig and self.STATEMANAGER_ENABLE
and self.statemanager.ifaceobjdict):
# Save a copy of new iface objects and dependency_graph
new_ifaceobjdict = dict(self.ifaceobjdict)
new_dependency_graph = dict(self.dependency_graph)
# Save a copy of new iface objects and dependency_graph
new_ifaceobjdict = dict(self.ifaceobjdict)
new_dependency_graph = dict(self.dependency_graph)
if self.STATEMANAGER_ENABLE and self.statemanager.ifaceobjdict:
# if old state is present, read old state and mark op for 'down'
# followed by 'up' aka: reload
# old interface config is read into self.ifaceobjdict
#
self.read_old_iface_config()
op = 'reload'
else:
@@ -825,7 +824,7 @@ class ifupdownMain(ifupdownBase):
# reinitialize dependency graph
self.dependency_graph = OrderedDict({})
# Generate dependency info for old config
self.populate_dependency_info(downops)
self.populate_dependency_info(downops, ifacedownlist)
self._sched_ifaces(ifacedownlist, downops)
else:
self.logger.debug('no interfaces to down ..')

View File

@@ -11,7 +11,9 @@ import collections
import logging
import glob
import re
import os
from iface import *
from template import templateEngine
class networkInterfaces():
@@ -19,22 +21,24 @@ class networkInterfaces():
auto_ifaces = []
callbacks = {}
ifaces_file = "/etc/network/interfaces"
def __init__(self):
def __init__(self, interfacesfile='/etc/network/interfaces',
template_engine=None, template_lookuppath=None):
self.logger = logging.getLogger('ifupdown.' +
self.__class__.__name__)
self.callbacks = {'iface_found' : None,
'validate' : None}
self.allow_classes = {}
self._filestack = [self.ifaces_file]
self.interfacesfile = interfacesfile
self._filestack = [self.interfacesfile]
self._template_engine = templateEngine(template_engine,
template_lookuppath)
@property
def _currentfile(self):
try:
return self._filestack[-1]
except:
return self.ifaces_file
return self.interfacesfile
def _parse_error(self, filename, lineno, msg):
if lineno == -1:
@@ -95,12 +99,14 @@ class networkInterfaces():
[self.auto_ifaces.append(a) for a in auto_ifaces]
return 0
def _add_to_iface_config(self, iface_config, attrname, attrval, lineno):
def _add_to_iface_config(self, ifacename, iface_config, attrname,
attrval, lineno):
newattrname = attrname.replace("_", "-")
try:
if not self.callbacks.get('validate')(newattrname, attrval):
self._parse_error(self._currentfile, lineno,
'unsupported keyword (%s)' %attrname)
'iface %s: unsupported keyword (%s)'
%(ifacename, attrname))
return
except:
pass
@@ -152,12 +158,12 @@ class networkInterfaces():
attrs = l.split(' ', 1)
if len(attrs) < 2:
self._parse_error(self._currentfile, line_idx,
'invalid syntax \'%s\'' %ifacename)
'iface %s: invalid syntax \'%s\'' %(ifacename, l))
continue
attrname = attrs[0]
attrval = attrs[1].strip(' ')
self._add_to_iface_config(iface_config, attrname, attrval,
line_idx+1)
self._add_to_iface_config(ifacename, iface_config, attrname,
attrval, line_idx+1)
lines_consumed = line_idx - cur_idx
# Create iface object
@@ -228,39 +234,27 @@ class networkInterfaces():
line_idx += 1
return 0
def run_template_engine(self, textdata):
try:
from mako.template import Template
except:
self.logger.warning('template engine mako not found. ' +
'skip template parsing ..');
return textdata
t = Template(text=textdata, output_encoding='utf-8')
return t.render()
def read_file(self, filename=None):
ifaces_file = filename
if not ifaces_file:
ifaces_file=self.ifaces_file
self._filestack.append(ifaces_file)
self.logger.info('reading interfaces file %s' %ifaces_file)
f = open(ifaces_file)
interfacesfile = filename
if not interfacesfile:
interfacesfile=self.interfacesfile
self._filestack.append(interfacesfile)
self.logger.info('reading interfaces file %s' %interfacesfile)
f = open(interfacesfile)
filedata = f.read()
f.close()
# process line continuations
filedata = ' '.join(d.strip() for d in filedata.split('\\'))
# run through template engine
try:
self.logger.info('template processing on interfaces file %s ...'
%ifaces_file)
rendered_filedata = self.run_template_engine(filedata)
rendered_filedata = self._template_engine.render(filedata)
except Exception, e:
self._parse_error(self._currentfile, -1,
'failed to render template (%s).' %str(e) +
'failed to render template (%s). ' %str(e) +
'Continue without template rendering ...')
rendered_filedata = None
pass
self.logger.info('parsing interfaces file %s ...' %ifaces_file)
self.logger.info('parsing interfaces file %s ...' %interfacesfile)
if rendered_filedata:
self.process_filedata(rendered_filedata)
else:

51
pkg/template.py Normal file
View File

@@ -0,0 +1,51 @@
#!/usr/bin/python
import logging
import traceback
from utils import *
class templateEngine():
def __init__(self, template_engine, template_lookuppath=None):
self.logger = logging.getLogger('ifupdown.' +
self.__class__.__name__)
self.tclass = None
self.tclassargs = {}
self.render = self._render_default
if template_engine == 'mako':
try:
self.tclass = utils.importName('mako.template', 'Template')
except Exception, e:
self.logger.warn('unable to load template engine %s (%s)'
%(template_engine, str(e)))
pass
if template_lookuppath:
try:
self.logger.debug('setting template lookuppath to %s'
%template_lookuppath)
lc = utils.importName('mako.lookup', 'TemplateLookup')
self.tclassargs['lookup'] = lc(
directories=template_lookuppath.split(':'))
except Exception, e:
self.logger.warn('unable to set template lookup path' +
' %s (%s)' %(template_lookuppath, str(e)))
pass
self.render = self._render_mako
else:
self.logger.info('skip template processing.., ' +
'template engine not found')
def _render_default(self, textdata):
return textdata
def _render_mako(self, textdata):
""" render textdata passed as argument using mako
Returns rendered textdata """
if not self.tclass:
return textdata
self.logger.info('template processing on interfaces file ...')
t = self.tclass(text=textdata, output_encoding='utf-8',
lookup=self.tclassargs.get('lookup'))
return t.render()

13
pkg/utils.py Normal file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/python
class utils():
@classmethod
def importName(cls, modulename, name):
""" Import a named object """
try:
module = __import__(modulename, globals(), locals(), [name])
except ImportError:
return None
return getattr(module, name)

View File

@@ -5,11 +5,15 @@ import sys
import os
import argcomplete
import argparse
import ConfigParser
import StringIO
from ifupdown.ifupdownmain import *
import logging
lockfile="/run/network/.lock"
configfile="/etc/network/ifupdown2/ifupdown2.conf"
configmap_g=None
logger = None
ENVPATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
@@ -24,15 +28,15 @@ def run_up(args):
cachearg=(False if (iflist or args.nocache or
args.perfmode or args.noact)
else True)
ifupdown_handle = ifupdownMain(force=args.force,
ifupdown_handle = ifupdownMain(config=configmap_g,
force=args.force,
withdepends=args.withdepends,
perfmode=args.perfmode,
njobs=args.jobs,
dryrun=args.noact,
cache=cachearg,
addons_enable=not args.noaddons,
statemanager_enable=not args.noaddons)
statemanager_enable=not args.noaddons,
interfacesfile=args.interfacesfile)
if args.noaddons:
ifupdown_handle.up(['up'], args.all, args.CLASS, iflist,
excludepats=args.excludepats,
@@ -53,13 +57,13 @@ def run_down(args):
try:
iflist = args.iflist
logger.debug('creating ifupdown object ..')
ifupdown_handle = ifupdownMain(force=args.force,
ifupdown_handle = ifupdownMain(config=configmap_g, force=args.force,
withdepends=args.withdepends,
perfmode=args.perfmode,
njobs=args.jobs,
dryrun=args.noact,
addons_enable=not args.noaddons,
statemanager_enable=not args.noaddons)
statemanager_enable=not args.noaddons,
interfacesfile=args.interfacesfile)
ifupdown_handle.down(['pre-down', 'down', 'post-down'],
args.all, args.CLASS, iflist,
@@ -96,10 +100,11 @@ def run_query(args):
iflist = [i for i in os.listdir('/sys/class/net/')
if os.path.isdir('/sys/class/net/%s' %i)]
logger.debug('creating ifupdown object ..')
ifupdown_handle = ifupdownMain(withdepends=args.withdepends,
ifupdown_handle = ifupdownMain(config=configmap_g,
withdepends=args.withdepends,
perfmode=args.perfmode,
njobs=args.jobs,
cache=cachearg)
cache=cachearg,
interfacesfile=args.interfacesfile)
ifupdown_handle.query([qop], args.all, args.CLASS, iflist,
excludepats=args.excludepats,
@@ -113,13 +118,14 @@ def run_reload(args):
try:
logger.debug('creating ifupdown object ..')
ifupdown_handle = ifupdownMain(withdepends=args.withdepends,
perfmode=args.perfmode,
njobs=args.jobs)
ifupdown_handle = ifupdownMain(config=configmap_g,
withdepends=args.withdepends,
perfmode=args.perfmode)
ifupdown_handle.reload(['pre-up', 'up', 'post-up'],
['pre-down', 'down', 'post-down'],
args.all, None, None,
excludepats=args.excludepats)
excludepats=args.excludepats,
usecurrentconfig=args.usecurrentconfig)
except:
raise
@@ -161,20 +167,24 @@ def update_argparser(argparser):
help=argparse.SUPPRESS)
argparser.add_argument('--allow', dest='CLASS',
help='ignore non-\"allow-CLASS\" interfaces')
argparser.add_argument('--with-depends', dest='withdepends',
argparser.add_argument('-w', '--with-depends', dest='withdepends',
action='store_true', help='run with all dependent interfaces.'+
' This option is redundant when \'-a\' is specified. With ' +
'\'-a\' interfaces are always executed in dependency order')
argparser.add_argument('--perfmode', dest='perfmode',
action='store_true', help=argparse.SUPPRESS)
argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
default=-1, choices=range(1,12), help=argparse.SUPPRESS)
#argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
# default=-1, choices=range(1,12), help=argparse.SUPPRESS)
argparser.add_argument('--nocache', dest='nocache', action='store_true',
help=argparse.SUPPRESS)
argparser.add_argument('-X', '--exclude', dest='excludepats',
action='append',
help='Exclude interfaces from the list of interfaces' +
' to operate on. Can be specified multiple times.')
argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
default='/etc/network/interfaces',
help='use interfaces file instead of default ' +
'/etc/network/interfaces')
def update_ifupdown_argparser(argparser):
""" common arg parser for ifup and ifdown """
@@ -185,13 +195,13 @@ def update_ifupdown_argparser(argparser):
group.add_argument('-n', '--no-act', dest='noact',
action='store_true', help='print out what would happen,' +
'but don\'t do it')
group.add_argument('--print-dependency',
group.add_argument('-p', '--print-dependency',
dest='printdependency', choices=['list', 'dot'],
help='print iface dependency')
group.add_argument('--no-scripts', '--no-addons',
dest='noaddons', action='store_true',
help='dont run any addon modules or scripts. Runs only link ' +
'up/down')
help='dont run any addon modules/scripts. Only bring the ' +
'interface administratively up/down')
def update_ifup_argparser(argparser):
argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck',
@@ -203,12 +213,11 @@ def update_ifdown_argparser(argparser):
update_ifupdown_argparser(argparser)
argparser.add_argument('--use-current-config',
dest='usecurrentconfig', action='store_true',
help=argparse.SUPPRESS)
#help='By default ifdown looks at the saved state for ' +
#'interfaces to bring down. This option allows ifdown to ' +
#'look at the current interfaces file. Useful when your ' +
#'state file is corrupted or you want down to use the latest '
#'from the interfaces file')
help='By default ifdown looks at the saved state for ' +
'interfaces to bring down. This option allows ifdown to ' +
'look at the current interfaces file. Useful when your ' +
'state file is corrupted or you want down to use the latest '
'from the interfaces file')
def update_ifquery_argparser(argparser):
""" arg parser for ifquery options """
@@ -229,12 +238,13 @@ def update_ifquery_argparser(argparser):
group.add_argument('--print-savedstate', action='store_true',
dest='printsavedstate',
help=argparse.SUPPRESS)
argparser.add_argument('--format', dest='format', default='native',
choices=['native', 'json'], help=argparse.SUPPRESS)
argparser.add_argument('--print-dependency',
argparser.add_argument('-m', '--format', dest='format', default='native',
choices=['native', 'json'],
help='interface display format')
argparser.add_argument('-p', '--print-dependency',
dest='printdependency', choices=['list', 'dot'],
help='print interface dependency')
argparser.add_argument('--syntax-help', action='store_true',
argparser.add_argument('-s', '--syntax-help', action='store_true',
dest='syntaxhelp',
help='print supported interface config syntax')
@@ -251,7 +261,7 @@ def update_ifreload_argparser(argparser):
argparser.add_argument('-d', '--debug', dest='debug',
action='store_true',
help='output debug info')
argparser.add_argument('--with-depends', dest='withdepends',
argparser.add_argument('-w', '--with-depends', dest='withdepends',
action='store_true', help=argparse.SUPPRESS)
argparser.add_argument('--perfmode', dest='perfmode',
action='store_true', help=argparse.SUPPRESS)
@@ -260,8 +270,19 @@ def update_ifreload_argparser(argparser):
argparser.add_argument('-X', '--exclude', dest='excludepats',
action='append',
help=argparse.SUPPRESS)
argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
default=-1, choices=range(1,12), help=argparse.SUPPRESS)
#argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
# default=-1, choices=range(1,12), help=argparse.SUPPRESS)
argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
default='/etc/network/interfaces',
help='use interfaces file instead of default ' +
'/etc/network/interfaces')
argparser.add_argument('--use-current-config',
dest='usecurrentconfig', action='store_true',
help='By default ifreload looks at saved state for ' +
'interfaces to bring down. With this option ifreload will'
' only look at the current interfaces file. Useful when your ' +
'state file is corrupted or you want down to use the latest '
'from the interfaces file')
def parse_args(argsv, op):
if op == 'query':
@@ -283,9 +304,7 @@ def parse_args(argsv, op):
update_ifquery_argparser(argparser)
elif op == 'reload':
update_ifreload_argparser(argparser)
argcomplete.autocomplete(argparser)
return argparser.parse_args(argsv)
handlers = {'up' : run_up,
@@ -308,6 +327,16 @@ def validate_args(op, args):
return False
return True
def read_config():
global configmap_g
config = open(configfile, 'r').read()
configStr = '[ifupdown2]\n' + config
configFP = StringIO.StringIO(configStr)
parser = ConfigParser.RawConfigParser()
parser.readfp(configFP)
configmap_g = dict(parser.items('ifupdown2'))
def main(argv):
""" main function """
args = None
@@ -329,6 +358,7 @@ def main(argv):
args = parse_args(argv[1:], op)
if not validate_args(op, args):
exit(1)
read_config()
init(args)
handlers.get(op)(args)
except Exception, e:

View File

@@ -16,6 +16,8 @@ setup(name='ifupdown2',
('/etc/init.d/',
['init.d/networking']),
('/sbin/', ['sbin/ifupdown']),
('/etc/network/ifupdown2/',
['config/ifupdown2.conf', 'config/templates']),
('/usr/share/doc/ifupdown/examples/',
['docs/examples/interfaces'])]
)