From 739f665baab47ab1465b55f9a1d55d301b07b28a Mon Sep 17 00:00:00 2001 From: roopa Date: Thu, 16 Jan 2014 06:46:17 -0800 Subject: [PATCH] A whole lot of fixes and some new code (needs some cleanup which will be part of subsequent checkins) Ticket: CM-1438 Reviewed By: Testing Done: Tested ifup, ifdown and ifquery Conflicts: packages/ifupdown2-addons/addons/ifenslave.py --- README | 9 + debian/python-ifupdown2.postinst | 1 + pkg/graph.py | 49 ++++ pkg/iface.py | 29 ++- pkg/ifupdownbase.py | 3 +- pkg/ifupdownmain.py | 427 +++++++++++++++++++++++++------ pkg/networkinterfaces.py | 7 +- pkg/scheduler.py | 9 +- sbin/ifupdown | 79 ++++-- 9 files changed, 498 insertions(+), 115 deletions(-) diff --git a/README b/README index a2c2ad6..6af7ffe 100644 --- a/README +++ b/README @@ -54,3 +54,12 @@ install instructions dpkg -i python-ifupdown2-addons-.deb +extra packages to use addional packages: +====================================== + +To use templates install python-mako (from wheezy): + apt-get install python-mako + +To generate dot files install python-gvgen (from wheezy): + apt-get install python-gvgen + diff --git a/debian/python-ifupdown2.postinst b/debian/python-ifupdown2.postinst index 2f5f7cf..4d329a9 100644 --- a/debian/python-ifupdown2.postinst +++ b/debian/python-ifupdown2.postinst @@ -51,6 +51,7 @@ case "$1" in [ -e /sbin/ifup ] || ln -s /sbin/ifupdown /sbin/ifup [ -e /sbin/ifdown ] || ln -s /sbin/ifupdown /sbin/ifdown [ -e /sbin/ifquery ] || ln -s /sbin/ifupdown /sbin/ifquery + [ -e /sbin/ifreload ] || ln -s /sbin/ifupdown /sbin/ifreload ;; purge) diff --git a/pkg/graph.py b/pkg/graph.py index 8a784bb..a3e5867 100644 --- a/pkg/graph.py +++ b/pkg/graph.py @@ -9,6 +9,10 @@ import logging from collections import deque +try: + from gvgen import * +except ImportError, e: + pass class graph(): @@ -49,3 +53,48 @@ class graph(): ' (indegree %d)' %indegree) return S + + @classmethod + def add_to_dot_old(cls, dependency_graph, gvgraph, v, parentgvitem): + dependents = dependency_graph.get(v, []) + if dependents is None: + return + if len(dependents) > 1: + # if more than one dependents .., add them to a box + box = gvgraph.newItem(v) + for d in dependents: + dnode = gvgraph.newItem(d, box) + cls.add_to_dot(dependency_graph, gvgraph, d, dnode) + if parentgvitem is not None: gvgraph.newLink(parentgvitem, + dnode) + else: + for d in dependents: + dnode = gvgraph.newItem(d) + cls.add_to_dot(dependency_graph, gvgraph, d, dnode) + if parentgvitem is not None: gvgraph.newLink(parentgvitem, + dnode) + + @classmethod + def add_to_dot(cls, dependency_graph, gvgraph, v, parentgvitem): + vnode = gvgraph.newItem(v) + if parentgvitem is not None: gvgraph.newLink(parentgvitem, vnode) + dependents = dependency_graph.get(v, []) + if dependents is None: + return + for d in dependents: + cls.add_to_dot(dependency_graph, gvgraph, d, vnode) + + @classmethod + def generate_dot(cls, dependency_graph, v): + gvgraph = GvGen() + cls.add_to_dot(dependency_graph, gvgraph, v, None) + gvgraph.dot(name=v) + + @classmethod + def generate_dots(cls, dependency_graph, indegrees): + roots = [k for k, v in indegrees.items() if v == 0] + if roots is None: + return + print roots + map(lambda r: cls.generate_dot(dependency_graph, r), roots) + diff --git a/pkg/iface.py b/pkg/iface.py index a9b217d..61d4d22 100644 --- a/pkg/iface.py +++ b/pkg/iface.py @@ -31,6 +31,8 @@ class ifaceStatus(): return 'success' elif state == cls.ERROR: return 'error' + elif state == cls.NOTFOUND: + return 'not found' @classmethod def from_str(cls, state_str): @@ -104,7 +106,12 @@ class iface(): self.status = ifaceStatus.UNKNOWN self.flags = 0x0 self.refcnt = 0 + # dependents that are listed as in the + # config file self.dependents = None + # All dependents (includes dependents that + # are not listed in the config file) + self.realdev_dependents = None self.auto = False self.classes = [] self.env = None @@ -113,7 +120,6 @@ class iface(): self.linkstate = None def inc_refcnt(self): - #print 'inside inc_refcnt = %d' %self.refcnt self.refcnt += 1 def dec_refcnt(self): @@ -217,6 +223,12 @@ class iface(): def get_dependents(self): return self.dependents + def set_realdev_dependents(self, dlist): + self.realdev_dependents = dlist + + def get_realdev_dependents(self): + return self.realdev_dependents + def set_linkstate(self, l): self.linkstate = l @@ -268,7 +280,16 @@ class iface(): if len(env) > 0: self.set_env(env) - def update_config(self, attr_name, attr_value, attr_status=0): + def update_config(self, attr_name, attr_value): + if self.config.get(attr_name) is None: + self.config[attr_name] = [attr_value] + else: + self.config[attr_name].append(attr_value) + + def update_config_dict(self, attrdict): + self.config.update(attrdict) + + def update_config_with_status(self, attr_name, attr_value, attr_status=0): if attr_value is None: attr_value = '' @@ -314,6 +335,10 @@ class iface(): else: logger.info(indent + 'dependents: None') + logger.info(indent + 'realdev dependents: %s' + %str(self.get_realdev_dependents())) + + logger.info(indent + 'config: ') config = self.get_config() if config is not None: diff --git a/pkg/ifupdownbase.py b/pkg/ifupdownbase.py index d1d0d02..a639f3e 100644 --- a/pkg/ifupdownbase.py +++ b/pkg/ifupdownbase.py @@ -56,7 +56,8 @@ class ifupdownBase(object): def log_error(self, str): if self.ignore_error(str) == False: - raise Exception(str) + raise + #raise Exception(str) else: pass diff --git a/pkg/ifupdownmain.py b/pkg/ifupdownmain.py index 1a8084c..b697eca 100644 --- a/pkg/ifupdownmain.py +++ b/pkg/ifupdownmain.py @@ -53,6 +53,12 @@ class ifupdownMain(): OrderedDict([('pre-up', OrderedDict({})), ('up' , OrderedDict({})), ('post-up' , OrderedDict({}))]), + 'query-checkcurr' : + OrderedDict([('query-checkcurr', OrderedDict({}))]), + + 'query-running' : + OrderedDict([('query-running', OrderedDict({}))]), + 'down' : OrderedDict([('pre-down', OrderedDict({})), ('down' , OrderedDict({})), @@ -60,7 +66,8 @@ class ifupdownMain(): def __init__(self, force=False, dryrun=False, nowait=False, - perfmode=False, nodepends=False, njobs=1): + perfmode=False, nodepends=False, njobs=1, + format='nwifaces', cache=False): self.logger = logging.getLogger('ifupdown') self.FORCE = force @@ -68,6 +75,8 @@ class ifupdownMain(): self.NOWAIT = nowait self.PERFMODE = perfmode self.NODEPENDS = nodepends + self.CACHE = cache + self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = True self.ifaces = OrderedDict() self.njobs = njobs @@ -114,6 +123,16 @@ class ifupdownMain(): def get_dryrun(self): return self.DRYRUN + def get_cache(self): + return self.CACHE + + def get_ifaceobjdict(self): + return self.ifaceobjdict + + def set_ifaceobjdict(self, ifaceobjdict): + del self.ifaceobjdict + self.ifaceobjdict = ifaceobjdict + def set_perfmode(self, perfmode): if perfmode == True: self.logger.debug('setting perfmode to true') @@ -161,20 +180,26 @@ class ifupdownMain(): def get_iface_obj_last(self, ifacename): return self.ifaceobjdict.get(ifacename)[-1] - def create_ifaceobjcurr(self, ifacename): - ifaceobj = self.get_ifaceobjcurr(ifacename) - if ifaceobj is not None: - return ifaceobj + def create_ifaceobjcurr(self, ifaceobj): + ifacename = ifaceobj.get_name() + ifaceobjcurr = self.get_ifaceobjcurr(ifacename) + if ifaceobjcurr is not None: + return ifaceobjcurr - ifaceobj = iface() - ifaceobj.set_name(ifacename) - self.ifaceobjcurrdict[ifacename] = ifaceobj + ifaceobjcurr = iface() + ifaceobjcurr.set_name(ifacename) + ifaceobjcurr.set_dependents(ifaceobj.get_dependents()) + self.ifaceobjcurrdict[ifacename] = ifaceobjcurr return ifaceobj def get_ifaceobjcurr(self, ifacename): return self.ifaceobjcurrdict.get(ifacename) + + def get_ifaceobjrunning(self, ifacename): + return self.ifaceobjrunningdict.get(ifacename) + def get_iface_status(self, ifacename): ifaceobjs = self.get_iface_objs(ifacename) for i in ifaceobjs: @@ -191,19 +216,16 @@ class ifupdownMain(): max = i.get_refcnt() return max - def create_fake_vlan_iface(self, vlan_ifname, op): + def create_n_save_ifaceobj(self, ifacename, increfcnt=False): """ creates and returns a fake vlan iface object. - - This was added to support creation of simple vlan devices without any - user specified configuration. - + This was added to support creation of simple vlan + devices without any user specified configuration. """ - - # XXX: Ideally this should be a call-back into the vlan module. - vlan_iface_obj = iface() - vlan_iface_obj.set_name(vlan_ifname) - - return vlan_iface_obj + ifaceobj = iface() + ifaceobj.set_name(ifacename) + if increfcnt == True: + ifaceobj.inc_refcnt() + self.ifaceobjdict[ifacename] = [ifaceobj] def is_vlan_device(self, ifacename): """ Returns true if iface name is a vlan interface. @@ -216,20 +238,31 @@ class ifupdownMain(): return False def preprocess_dependency_list(self, dlist, op): + """ We go through the dependency list and + delete or add interfaces from the interfaces dict by + applying the following rules: + if flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True: + we only consider devices whose configuration was + specified in the network interfaces file. We delete + any interface whose config was not specified except + for vlan devices. vlan devices get special treatment. + Even if they are not present they are created and added + to the ifacesdict + elif flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False: + we create objects for all dependent devices that are not + present in the ifacesdict + """ del_list = [] + create_list = [] self.logger.debug('pre-processing dependency list: %s' %list(dlist)) for d in dlist: dilist = self.get_iface_objs(d) if dilist == None: - if self.is_vlan_device(d) == True: - vlan_iface_obj = self.create_fake_vlan_iface(d, op) - - # Add face vlan device to ifaceobjdict dict - vlan_iface_obj.inc_refcnt() - self.save_iface(vlan_iface_obj) + if (self.is_vlan_device(d) == True or + self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG == False): + create_list.append(d) else: - # Remove the device from the list del_list.append(d) else: for di in dilist: @@ -238,10 +271,13 @@ class ifupdownMain(): for d in del_list: dlist.remove(d) + # create fake devices to all dependents that dont have config + map(lambda i: self.create_n_save_ifaceobj(i, increfcnt=True), + create_list) + self.logger.debug('After Processing dependency list: %s' %list(dlist)) - def get_dependents(self, ifaceobj, op): """ Gets iface dependents by calling into respective modules """ dlist = None @@ -254,16 +290,23 @@ class ifupdownMain(): for mname, mdata in mdict.items(): if mdata.get('ftype') == 'pmodule': module = mdata.get('module') - if (hasattr(module, - 'get_dependent_ifacenames') == False): - continue - dlist = module.get_dependent_ifacenames(ifaceobj, - self.ifaceobjdict.keys()) + if op == 'query-running': + if (hasattr(module, + 'get_dependent_ifacenames_running') == False): + continue + dlist = module.get_dependent_ifacenames_running( + ifaceobj) + else: + if (hasattr(module, + 'get_dependent_ifacenames') == False): + continue + dlist = module.get_dependent_ifacenames(ifaceobj, + self.ifaceobjdict.keys()) if dlist is not None: + ifaceobj.set_realdev_dependents(dlist[:]) self.logger.debug('%s: ' %ifaceobj.get_name() + 'got dependency list: %s' %str(dlist)) break - return dlist def generate_dependency_info(self, ifacenames, dependency_graph, op): @@ -290,8 +333,7 @@ class ifupdownMain(): if dlist is not None: self.preprocess_dependency_list(dlist, op) ifaceobj.set_dependents(dlist) - for d in dlist: - iqueue.append(d) + [iqueue.append(d) for d in dlist] if dependency_graph.get(i) is None: dependency_graph[i] = dlist @@ -330,7 +372,12 @@ class ifupdownMain(): """ - mmetadata = self.operations[mkind][msubkind].get(mname) + try: + mmetadata = self.operations[mkind][msubkind].get(mname) + except KeyError: + self.logger.warn('unsupported module type %s' %mname) + return + if mmetadata is None or mmetadata.get('ftype') != 'pmodule': mmetadata = {} mmetadata['ftype'] = mftype @@ -373,15 +420,20 @@ class ifupdownMain(): minstance = mclass(force=self.get_force(), dryrun=self.get_dryrun(), nowait=self.get_nowait(), - perfmode=self.get_perfmode()) + perfmode=self.get_perfmode(), + cache=self.get_cache()) ops = minstance.get_ops() for op in ops: - if re.search('up', op) is not None: + if re.search('query', op) is not None: + self.save_module(op, op, mname, 'pmodule', + minstance) + elif re.search('up', op) is not None: self.save_module('up', op, mname, 'pmodule', minstance) else: - self.save_module('down', op, mname, 'pmodule', - minstance) + self.save_module('down', op, mname, + 'pmodule', minstance) + except: raise @@ -396,6 +448,9 @@ class ifupdownMain(): self.logger.info('loading user modules from %s' %modules_dir) for op, subops in self.operations.items(): + if re.search('query', op) is not None: + continue + for subop in subops.keys(): msubdir = modules_dir + '/if-%s.d' %subop self.logger.info('loading modules under %s ...' %msubdir) @@ -475,11 +530,27 @@ class ifupdownMain(): ret = ifaceSched.run_iface_dependency_graph_parallel(self, dependency_graph, op) else: - ret = ifaceSched.run_iface_dependency_graph(self, dependency_graph, - op) - + ret = ifaceSched.run_iface_dependency_graph(self, + dependency_graph, op) return ret + def print_dependency(self, op, ifacenames, format): + dependency_graph = {} + if ifacenames is None: + ifacenames = self.ifaceobjdict.keys() + + # generate dependency graph of interfaces + self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False + self.generate_dependency_info(ifacenames, dependency_graph, op) + + if format == 'list': + self.pp.pprint(dependency_graph) + elif format == 'dot': + indegrees = {} + map(lambda i: indegrees.update({i : + self.get_iface_refcnt(i)}), + dependency_graph.keys()) + graph.generate_dots(dependency_graph, indegrees) def validate_ifaces(self, ifacenames): """ validates interface list for config existance. @@ -539,10 +610,8 @@ class ifupdownMain(): return True def generate_running_env(self, ifaceobj, op): - """ Generates a dictionary with env variables required for an interface. - - Used to support script execution for interfaces. - + """ Generates a dictionary with env variables required for + an interface. Used to support script execution for interfaces. """ cenv = None @@ -560,8 +629,8 @@ class ifupdownMain(): def run(self, op, auto=False, allow_classes=None, - ifacenames=None, query_state=None, excludepats=None, - format=None): + ifacenames=None, excludepats=None, + format=None, printdependency=None): """ main ifupdown run method """ if auto == True: @@ -571,12 +640,15 @@ class ifupdownMain(): # Only read new iface config for 'up' # operations. For 'downs' we only rely on # old state - if op == 'up' or op == 'query': + if op == 'query-running': + # create fake devices to all dependents that dont have config + map(lambda i: self.create_n_save_ifaceobj(i), ifacenames) + elif op == 'up' or op[:5] == 'query': try: self.read_iface_config() except Exception, e: raise - else: + elif op == 'down': # for down we need to look at old state self.logger.debug('down op, looking at old state ..') @@ -584,18 +656,19 @@ class ifupdownMain(): self.read_old_iface_config() elif self.FORCE == True: # If no old state available - self.logger.info('old state not available. Force option ' + - 'set. Loading new iface config file') + self.logger.info('old state not available. ' + + 'Force option set. Loading new iface config file') try: self.read_iface_config() except Exception, e: - raise Exception('error reading iface config (%s)' %str(e)) + raise Exception('error reading iface config (%s)' + %str(e)) else: raise Exception('old state not available...aborting.' + ' try running with --force option') - if ifacenames is not None: + if ifacenames is not None and op != 'query-running': # If iface list is given, always check if iface is present if self.validate_ifaces(ifacenames) != 0: raise Exception('all or some interfaces not found') @@ -604,36 +677,45 @@ class ifupdownMain(): if ifacenames is None: ifacenames = self.ifaceobjdict.keys() # filter interfaces based on auto and allow classes - filtered_ifacenames = [i for i in ifacenames - if self.iface_whitelisted(auto, allow_classes, excludepats, - i) == True] + if op == 'query-running': + filtered_ifacenames = ifacenames + else: + filtered_ifacenames = [i for i in ifacenames + if self.iface_whitelisted(auto, allow_classes, + excludepats, i) == True] if len(filtered_ifacenames) == 0: raise Exception('no ifaces found matching ' + 'given allow lists') if op == 'query': - if query_state == None: - return self.print_ifaceobjs_pretty(filtered_ifacenames) - elif query_state == 'presumed': - return self.print_ifaceobjs_saved_state_pretty( + return self.print_ifaceobjs_raw(filtered_ifacenames) + elif op == 'query-presumed': + return self.print_ifaceobjs_saved_state_pretty( filtered_ifacenames) - elif query_state == 'presumeddetailed': - return self.print_ifaceobjs_saved_state_detailed_pretty( + elif op == 'query-presumeddetailed': + return self.print_ifaceobjs_saved_state_detailed_pretty( filtered_ifacenames) - if op == 'query' or self.NODEPENDS == True: + if printdependency is not None: + self.print_dependency(op, filtered_ifacenames, printdependency) + return + + #if op.split('-')[0] == 'query' or self.NODEPENDS == True: + if op.split('-')[0] == 'query' or self.NODEPENDS == True: self.run_without_dependents(op, filtered_ifacenames) else: self.run_with_dependents(op, filtered_ifacenames) - if op == 'query': - if query_state == 'curr': - # print curr state of all interfaces - ret = self.print_ifaceobjscurr_pretty(filtered_ifacenames) - if ret != 0: - # if any of the object has an error, signal that silently - raise Exception('') + if op == 'query-checkcurr': + # print curr state of all interfaces + ret = self.print_ifaceobjscurr_pretty(filtered_ifacenames) + if ret != 0: + # if any of the object has an error, signal that silently + raise Exception('') + elif op == 'query-running': + # print curr state of all interfaces + self.print_ifaceobjsrunning_pretty(filtered_ifacenames) return # Update persistant iface states @@ -649,17 +731,162 @@ class ifupdownMain(): self.logger.warning('error saving state (%s)' %str(e)) - def up(self, auto=False, allow=None, ifacenames=None, excludepats=None): - return self.run('up', auto, allow, ifacenames, excludepats=excludepats) + def up(self, auto=False, allow=None, ifacenames=None, + excludepats=None, printdependency=None): + return self.run('up', auto, allow, ifacenames, + excludepats=excludepats, + printdependency=printdependency) def down(self, auto=False, allow=None, ifacenames=None, excludepats=None): return self.run('down', auto, allow, ifacenames, excludepats=excludepats); - def query(self, auto=False, allow=None, ifacenames=None, - query_state=False, excludepats=None): - return self.run('query', auto, allow, ifacenames, - query_state=query_state, excludepats=excludepats); + def query(self, op, auto=False, allow_classes=None, ifacenames=None, + excludepats=None, printdependency=None, + format=None): + """ main ifupdown run method """ + if auto == True: + self.logger.debug('setting flag ALL') + self.ALL = True + + if op == 'query-running': + self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False + # create fake devices to all dependents that dont have config + map(lambda i: self.create_n_save_ifaceobj(i), ifacenames) + else: + try: + self.read_iface_config() + except Exception: + raise + + if ifacenames is not None and op != 'query-running': + # If iface list is given, always check if iface is present + if self.validate_ifaces(ifacenames) != 0: + raise Exception('all or some interfaces not found') + + # if iface list not given by user, assume all from config file + if ifacenames is None: ifacenames = self.ifaceobjdict.keys() + + # filter interfaces based on auto and allow classes + if op == 'query-running': + filtered_ifacenames = ifacenames + else: + filtered_ifacenames = [i for i in ifacenames + if self.iface_whitelisted(auto, allow_classes, + excludepats, i) == True] + + if len(filtered_ifacenames) == 0: + raise Exception('no ifaces found matching ' + + 'given allow lists') + + if op == 'query': + return self.print_ifaceobjs_raw(filtered_ifacenames) + elif op == 'query-presumed': + return self.print_ifaceobjs_saved_state_pretty( + filtered_ifacenames) + elif op == 'query-presumeddetailed': + return self.print_ifaceobjs_saved_state_detailed_pretty( + filtered_ifacenames) + + if printdependency is not None: + self.print_dependency(op, filtered_ifacenames, printdependency) + return + + if self.NODEPENDS == True: + self.run_without_dependents(op, filtered_ifacenames) + else: + self.run_with_dependents(op, filtered_ifacenames) + + if op == 'query-checkcurr': + ret = self.print_ifaceobjscurr_pretty(filtered_ifacenames) + if ret != 0: + # if any of the object has an error, signal that silently + raise Exception('') + elif op == 'query-running': + self.print_ifaceobjsrunning_pretty(filtered_ifacenames) + return + + def reload(self, auto=False, allow=None, + ifacenames=None, excludepats=None): + """ main ifupdown run method """ + allow_classes = [] + + self.logger.debug('reloading interface config ..') + + if auto == True: + self.logger.debug('setting flag ALL') + self.ALL = True + + try: + self.read_iface_config() + except Exception, e: + raise + + # Save a copy of new iface objects + new_ifaceobjdict = self.get_ifaceobjdict() + + if len(self.statemanager.get_ifaceobjdict()) > 0: + # if old state is present, read old state and mark op for 'down' + # followed by 'up' aka: reload + self.read_old_iface_config() + op = 'reload' + else: + # oldconfig not available, continue with 'up' with new config + op = 'up' + + if ifacenames is None: ifacenames = self.ifaceobjdict.keys() + if (op == 'reload' and ifacenames is not None and + len(ifacenames) != 0): + filtered_ifacenames = [i for i in ifacenames + if self.iface_whitelisted(auto, allow_classes, + excludepats, i) == True] + ifacedownlist = Set(filtered_ifacenames).difference( + Set(new_ifaceobjdict.keys())) + if ifacedownlist is not None and len(ifacedownlist) > 0: + self.logger.debug('bringing down interfaces: %s' + %str(ifacedownlist)) + if self.NODEPENDS == True: + self.run_without_dependents('down', ifacedownlist) + else: + self.run_with_dependents('down', ifacedownlist) + + # Update persistant iface states + try: + if self.ALL == True: + self.statemanager.flush_state(self.ifaceobjdict) + else: + self.statemanager.flush_state() + except Exception, e: + if self.logger.isEnabledFor(logging.DEBUG): + t = sys.exc_info()[2] + traceback.print_tb(t) + self.logger.warning('error saving state (%s)' %str(e)) + + # Now, run up with new dict + self.set_ifaceobjdict(new_ifaceobjdict) + ifacenames = self.ifaceobjdict.keys() + filtered_ifacenames = [i for i in ifacenames + if self.iface_whitelisted(auto, allow_classes, + excludepats, i) == True] + + self.logger.debug('bringing up interfaces: %s' + %str(filtered_ifacenames)) + if self.NODEPENDS == True: + self.run_without_dependents('up', filtered_ifacenames) + else: + self.run_with_dependents('up', filtered_ifacenames) + + # Update persistant iface states + try: + if self.ALL == True: + self.statemanager.flush_state(self.get_ifaceobjdict()) + else: + self.statemanager.flush_state() + except Exception, e: + if self.logger.isEnabledFor(logging.DEBUG): + t = sys.exc_info()[2] + traceback.print_tb(t) + self.logger.warning('error saving state (%s)' %str(e)) def dump(self): """ all state dump """ @@ -672,24 +899,37 @@ class ifupdownMain(): def print_state(self, ifacenames=None): self.statemanager.dump(ifacenames) + def print_ifaceobjs_raw(self, ifacenames): + for i in ifacenames: + ifaceobjs = self.get_iface_objs(i) + for i in ifaceobjs: + i.dump_raw(self.logger) + print '\n' + def print_ifaceobjs_pretty(self, ifacenames): for i in ifacenames: ifaceobjs = self.get_iface_objs(i) - for io in ifaceobjs: - io.dump_raw(self.logger) + for i in ifaceobjs: + i.dump_pretty(self.logger) print '\n' - def print_ifaceobjscurr_pretty(self, ifacenames, format): + def dump_ifaceobjs(self, ifacenames): + for i in ifacenames: + ifaceobjs = self.get_iface_objs(i) + for i in ifaceobjs: + i.dump(self.logger) + print '\n' + + def print_ifaceobjscurr_pretty(self, ifacenames, format=None): """ Dumps current running state of interfaces. returns 1 if any of the interface has an error, else returns 0 - """ - ret = 0 for i in ifacenames: ifaceobj = self.get_ifaceobjcurr(i) + if ifaceobj is None: continue if ifaceobj.get_status() == ifaceStatus.NOTFOUND: print 'iface %s' %ifaceobj.get_name() + ' (not found)' ret = 1 @@ -701,8 +941,29 @@ class ifupdownMain(): else: ifaceobj.dump_json(self.logger) + dlist = ifaceobj.get_dependents() + if dlist is None or len(dlist) == 0: continue + self.print_ifaceobjscurr_pretty(dlist, format) + return ret + def print_ifaceobjsrunning_pretty(self, ifacenames, format=None): + for i in ifacenames: + ifaceobj = self.get_iface_obj_first(i) + if ifaceobj.get_status() == ifaceStatus.NOTFOUND: + print 'iface %s' %ifaceobj.get_name() + ' (not found)' + continue + + if format is None or format == 'nwifaces': + ifaceobj.dump_pretty(self.logger) + elif format == 'json': + ifaceobj.dump_json(self.logger) + + dlist = ifaceobj.get_dependents() + if dlist is None or len(dlist) == 0: continue + self.print_ifaceobjsrunning_pretty(dlist, format) + return + def print_ifaceobjs_saved_state_pretty(self, ifacenames): self.statemanager.print_state_pretty(ifacenames, self.logger) diff --git a/pkg/networkinterfaces.py b/pkg/networkinterfaces.py index 4bf3595..ef636b1 100644 --- a/pkg/networkinterfaces.py +++ b/pkg/networkinterfaces.py @@ -10,6 +10,7 @@ import collections import logging import glob +import re from iface import * class networkInterfaces(): @@ -95,6 +96,7 @@ class networkInterfaces(): iface_line = lines[cur_idx].strip('\n ') iface_attrs = iface_line.split() + ifacename = iface_attrs[1] ifaceobj.raw_lines.append(iface_line) @@ -111,6 +113,9 @@ class networkInterfaces(): ifaceobj.raw_lines.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('invalid syntax at line %d' %(line_idx + 1)) @@ -124,7 +129,7 @@ class networkInterfaces(): lines_consumed = line_idx - cur_idx # Create iface object - ifaceobj.set_name(iface_attrs[1]) + ifaceobj.set_name(ifacename) ifaceobj.set_config(iface_config) ifaceobj.generate_env() if len(iface_attrs) > 2: diff --git a/pkg/scheduler.py b/pkg/scheduler.py index 156b187..d768b6d 100644 --- a/pkg/scheduler.py +++ b/pkg/scheduler.py @@ -50,10 +50,10 @@ class ifaceScheduler(ifupdownBase): self.logger.debug('%s: ' %ifaceobj.get_name() + 'running module %s' %mname + ' op %s' %op + ' subop %s' %subop) - if op == 'query': - m.run(ifaceobj, subop, query=True, + if op == 'query-checkcurr': + m.run(ifaceobj, subop, query_check=True, query_ifaceobj=ifupdownobj.create_ifaceobjcurr( - ifaceobj.get_name())) + ifaceobj)) else: m.run(ifaceobj, subop) else: @@ -65,7 +65,7 @@ class ifaceScheduler(ifupdownBase): err = 1 self.log_error(str(e)) finally: - if op != 'query': + if op[:5] != 'query': if err == 1: ifupdownobj.set_iface_state(ifaceobj, ifaceState.from_str(subop), @@ -135,7 +135,6 @@ class ifaceScheduler(ifupdownBase): """ Runs interface list through sub operation handler. """ self.logger.debug('running sub operation %s on all given interfaces' %op) - iface_run_queue = deque(ifacenames) for i in range(0, len(iface_run_queue)): if op == 'up': diff --git a/sbin/ifupdown b/sbin/ifupdown index ca8c63e..142a71c 100755 --- a/sbin/ifupdown +++ b/sbin/ifupdown @@ -15,42 +15,58 @@ def run(args, op): logger.debug('args = %s' %str(args)) try: - logger.debug('creating ifupdown object ..') - if op == 'up' or op == 'down': - ifupdown_handle = ifupdownMain(force=args.force, - nodepends=args.nodepends, - perfmode=args.perfmode, - njobs=args.jobs, - dryrun=args.noact) - elif op == 'query': - ifupdown_handle = ifupdownMain(nodepends=args.nodepends, - perfmode=args.perfmode, - njobs=args.jobs, - format=args.format) - iflist = args.iflist if len(args.iflist) == 0: iflist = None + cachearg=False if iflist is not None or args.nocache == True else True + logger.debug('creating ifupdown object ..') + if op == 'up' or op == 'down' or op == 'reload': + ifupdown_handle = ifupdownMain(force=args.force, + nodepends=args.nodepends, + perfmode=args.perfmode, + njobs=args.jobs, + dryrun=args.noact, + cache=cachearg) + elif op == 'query': + ifupdown_handle = ifupdownMain(nodepends=args.nodepends, + perfmode=args.perfmode, + njobs=args.jobs, + format=args.format, + cache=cachearg) + logger.debug('calling %s' %op + ' for all interfaces ..') if op == 'up': ifupdown_handle.up(args.all, args.CLASS, iflist, - excludepats=args.excludepats) + excludepats=args.excludepats, + printdependency=args.printdependency) elif op == 'down': ifupdown_handle.down(args.all, args.CLASS, iflist, excludepats=args.excludepats) elif op == 'query': - if args.curstate == True: - qstate='curr' + if args.checkcurstate == True: + qtype='query-checkcurr' elif args.presumedstate == True: - qstate='presumed' + qtype='query-presumed' elif args.presumedstatedetailed == True: - qstate='presumeddetailed' + qtype='query-presumeddetailed' + elif args.runningstate == True: + if iflist is None: + iflist = [i for i in os.listdir('/sys/class/net/') + if os.path.isdir('/sys/class/net/%s' %i) == True] + print iflist + qtype='query-running' else: - qstate=None - ifupdown_handle.query(args.all, args.CLASS, iflist, - query_state=qstate, + qtype='query' + + ifupdown_handle.query(qtype, args.all, args.CLASS, iflist, excludepats=args.excludepats) + elif op == 'reload': + if iflist is not None: + raise Exception('iflist is currently not supported with reload') + + ifupdown_handle.reload(args.all, args.CLASS, iflist, + excludepats=args.excludepats) except: raise @@ -77,6 +93,7 @@ def deinit(): {} def update_argparser(argparser): + """ base parser """ argparser.add_argument('iflist', metavar='IFACE', nargs='*', help='interfaces list') argparser.add_argument('-v', '--verbose', dest='verbose', @@ -95,6 +112,11 @@ def update_argparser(argparser): 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('--nocache', dest='nocache', action='store_true', + help=argparse.SUPPRESS) + argparser.add_argument('--print-dependency', + dest='printdependency', choices=['list', 'dot'], + help=argparse.SUPPRESS) argparser.add_argument('-X', '--exclude', dest='excludepats', action='append', help='exclude interfaces from the list of ' + 'interfaces to operate on by a PATTERN ' @@ -121,10 +143,14 @@ def update_ifquery_argparser(argparser): argparser.add_argument('-l', '--list', action='store_true', dest='all', help='process all interfaces marked \"auto\"') group = argparser.add_mutually_exclusive_group(required=False) - group.add_argument('-r', '--running-state', dest='curstate', + group.add_argument('-r', '--running-state', dest='runningstate', action='store_true', help='query running state of an interface') + group.add_argument('-c', '--check-state', dest='checkcurstate', + action='store_true', + help='check running state of an interface') + # presumed-state is state maintained by ifupdown group.add_argument('--presumed-state', dest='presumedstate', action='store_true', help=argparse.SUPPRESS) @@ -138,6 +164,8 @@ def update_ifquery_argparser(argparser): group.add_argument('--format', dest='format', default='nwifaces', choices=['nwifaces', 'json'], help=argparse.SUPPRESS) +def update_ifreload_argparser(argparser): + update_ifupdown_argparser(argparser) def parse_args(argsv, op): descr = 'interface management' @@ -150,6 +178,8 @@ def parse_args(argsv, op): update_ifdown_argparser(argparser) elif op == 'query': update_ifquery_argparser(argparser) + elif op == 'reload': + update_ifreload_argparser(argparser) return argparser.parse_args(argsv) @@ -163,6 +193,8 @@ def main(argv): op = 'down' elif re.search(r'ifquery', argv[0]) != None: op = 'query' + elif re.search(r'ifreload', argv[0]) != None: + op = 'reload' else: print ('Unexpected executable.' + ' Should be \'ifup\' or \'ifdown\' or \'ifquery\'') @@ -171,7 +203,7 @@ def main(argv): # Command line arg parser args = parse_args(argv[1:], op) if len(args.iflist) > 0 and args.all is True: - print 'interface list and \'-a\' are mutually exclusive' + print 'interface list cannot be specified with all option' exit(1) init(args) @@ -184,6 +216,7 @@ def main(argv): raise else: logger.error(str(e)) + print '\nRerun the command with \'-d\' for a detailed errormsg' exit(1) finally: deinit()