From 29704612b0dbae8643aa1c37fdddfd31833c7bb0 Mon Sep 17 00:00:00 2001 From: roopa Date: Fri, 17 Jan 2014 23:10:12 -0800 Subject: [PATCH] some ifquery fixes + make the default to not follow dependents ( added a --with-depends option) Ticket: CM-1438 Reviewed By: Testing Done: still debating on the default behaviour for following dependents. for now not following dependents might be better. When all interfaces are selected, it always follows dependents --- pkg/graph.py | 48 ++++++++++++++- pkg/iface.py | 16 +++-- pkg/ifupdownmain.py | 147 +++++++++++++++++++++++--------------------- pkg/scheduler.py | 6 +- sbin/ifupdown | 38 ++++++++---- 5 files changed, 162 insertions(+), 93 deletions(-) diff --git a/pkg/graph.py b/pkg/graph.py index a3e5867..30538ab 100644 --- a/pkg/graph.py +++ b/pkg/graph.py @@ -21,7 +21,7 @@ class graph(): self.__class__.__name__) @classmethod - def topological_sort(cls, dependency_graph, indegrees=None): + def topological_sort_graphs_all(cls, dependency_graphs, indegrees): S = [] Q = deque() @@ -29,6 +29,37 @@ class graph(): if indegree == 0: Q.append(ifname) + while len(Q) != 0: + # initialize queue + x = Q.popleft() + + # Get dependents of x + dlist = dependency_graphs.get(x) + if dlist == None or len(dlist) == 0: + S.append(x) + continue + + for y in dlist: + indegrees[y] = indegrees.get(y) - 1 + if indegrees.get(y) == 0: + Q.append(y) + + S.append(x) + + for ifname,indegree in indegrees.items(): + if indegree != 0: + raise Exception('cycle found involving iface %s' %ifname + + ' (indegree %d)' %indegree) + + return S + + @classmethod + def topological_sort_graph(cls, dependency_graph, indegrees, rootifname): + S = [] + Q = deque() + + Q.append(rootifname) + while len(Q) != 0: # initialize queue x = Q.popleft() @@ -46,13 +77,26 @@ class graph(): S.append(x) + return S + + @classmethod + def topological_sort_graphs(cls, dependency_graphs, indegrees): + """ Sorts graph one at a time merges all the sorted graph + lists and returns a combined list + + """ + sorted_graphs_list = [] + for ifname,indegree in indegrees.items(): + if indegree == 0: + sorted_graphs_list += cls.topological_sort_graph( + dependency_graphs, indegrees, ifname) # If some indegrees are non zero, we have a cycle for ifname,indegree in indegrees.items(): if indegree != 0: raise Exception('cycle found involving iface %s' %ifname + ' (indegree %d)' %indegree) - return S + return sorted_graphs_list @classmethod def add_to_dot_old(cls, dependency_graph, gvgraph, v, parentgvitem): diff --git a/pkg/iface.py b/pkg/iface.py index 61d4d22..d137b6a 100644 --- a/pkg/iface.py +++ b/pkg/iface.py @@ -7,6 +7,7 @@ # interface object # +from collections import OrderedDict import logging import json @@ -100,7 +101,7 @@ class iface(): self.name = None self.addr_family = None self.addr_method = None - self.config = {} + self.config = OrderedDict() self.children = [] self.state = ifaceState.NEW self.status = ifaceStatus.UNKNOWN @@ -155,6 +156,12 @@ class iface(): def get_config(self): return self.config + def is_config_present(self): + if self.config is None: + return False + + return (len(self.config) != 0) + def set_config_current(self, config_current): self.config_current = config_current @@ -338,7 +345,6 @@ class iface(): logger.info(indent + 'realdev dependents: %s' %str(self.get_realdev_dependents())) - logger.info(indent + 'config: ') config = self.get_config() if config is not None: @@ -347,7 +353,10 @@ class iface(): def dump_pretty(self, logger): indent = '\t' - outbuf = 'iface %s' %self.get_name() + outbuf = '' + if self.get_auto(): + outbuf += 'auto %s\n' %self.get_name() + outbuf += 'iface %s' %self.get_name() if self.get_addr_family() is not None: outbuf += ' %s' %self.get_addr_family() @@ -367,6 +376,5 @@ class iface(): print outbuf - def dump_json(self, logger): json.dumps(self) diff --git a/pkg/ifupdownmain.py b/pkg/ifupdownmain.py index b697eca..92f9a98 100644 --- a/pkg/ifupdownmain.py +++ b/pkg/ifupdownmain.py @@ -25,7 +25,7 @@ from sets import Set class ifupdownMain(): # Flags - NODEPENDS = False + WITH_DEPENDS = False ALL = False STATE_CHECK = False @@ -66,7 +66,7 @@ class ifupdownMain(): def __init__(self, force=False, dryrun=False, nowait=False, - perfmode=False, nodepends=False, njobs=1, + perfmode=False, withdepends=False, njobs=1, format='nwifaces', cache=False): self.logger = logging.getLogger('ifupdown') @@ -74,7 +74,7 @@ class ifupdownMain(): self.DRYRUN = dryrun self.NOWAIT = nowait self.PERFMODE = perfmode - self.NODEPENDS = nodepends + self.WITH_DEPENDS = withdepends self.CACHE = cache self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = True @@ -156,12 +156,12 @@ class ifupdownMain(): def get_njobs(self): return self.njobs - def get_nodepends(self): - return self.NODEPENDS + def get_withdepends(self): + return self.WITH_DEPENDS - def set_nodepends(self, nodepends): - self.logger.debug('setting nodepends to true') - self.NODEPENDS = nodepends + def set_withdepends(self, withdepends): + self.logger.debug('setting withdepends to true') + self.WITH_DEPENDS = withdepends def set_iface_state(self, ifaceobj, state, status): ifaceobj.set_state(state) @@ -227,13 +227,14 @@ class ifupdownMain(): ifaceobj.inc_refcnt() self.ifaceobjdict[ifacename] = [ifaceobj] - def is_vlan_device(self, ifacename): - """ Returns true if iface name is a vlan interface. - - only supports vlan interfaces of the format . + def is_builtin_iface(self, ifacename): + """ Returns true if iface name is a builtin interface. + A builtin interface is an interface which ifupdown understands. + The following are currently considered builtin ifaces: + - vlan interfaces in the format . """ - if (re.search(r'\.', ifacename, 0) is not None): + if re.search(r'\.', ifacename, 0) is not None: return True return False @@ -259,7 +260,7 @@ class ifupdownMain(): for d in dlist: dilist = self.get_iface_objs(d) if dilist == None: - if (self.is_vlan_device(d) == True or + if (self.is_builtin_iface(d) == True or self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG == False): create_list.append(d) else: @@ -497,6 +498,7 @@ class ifupdownMain(): def run_without_dependents(self, op, ifacenames): + runifacenames = [] ifaceSched = ifaceScheduler(force=self.FORCE) self.logger.debug('run_without_dependents for op %s' %op + @@ -505,8 +507,30 @@ class ifupdownMain(): if ifacenames == None: raise ifupdownInvalidValue('no interfaces found') - return ifaceSched.run_iface_list(self, ifacenames, op) + # Even though we are running without dependents here, we will have + # to cover the builtin dependents. Because the only way builtin + # devices are created is when they are seen as dependents + for i in ifacenames: + dlist = self.get_dependents(self.get_iface_obj_first(i), op) + if dlist is None: + runifacenames.append(i) + continue + # If these dependents are builtin devices which dont require + # any config section, we must include them in + builtin_dependents = [d for d in dlist + if self.get_iface_objs(d) is None and + self.is_builtin_iface(d)] + if len(builtin_dependents) != 0: + self.logger.info('Adding builtin interfaces %s ' + 'to the list of interfaces to bringup ' + %builtin_dependents) + map(lambda x: self.create_n_save_ifaceobj(x), + builtin_dependents) + runifacenames += builtin_dependents + runifacenames.append(i) + + return ifaceSched.run_iface_list(self, runifacenames, op) def run_with_dependents(self, op, ifacenames): dependency_graph = {} @@ -627,7 +651,6 @@ class ifupdownMain(): return cenv - def run(self, op, auto=False, allow_classes=None, ifacenames=None, excludepats=None, format=None, printdependency=None): @@ -636,14 +659,12 @@ class ifupdownMain(): if auto == True: self.logger.debug('setting flag ALL') self.ALL = True + self.WITH_DEPENDS = True # Only read new iface config for 'up' # operations. For 'downs' we only rely on # old state - 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': + if op == 'up': try: self.read_iface_config() except Exception, e: @@ -668,8 +689,9 @@ class ifupdownMain(): ' try running with --force option') - if ifacenames is not None and op != 'query-running': - # If iface list is given, always check if iface is present + if ifacenames is not None: + # If iface list is given by the caller, always check if iface + # is present if self.validate_ifaces(ifacenames) != 0: raise Exception('all or some interfaces not found') @@ -677,46 +699,21 @@ class ifupdownMain(): 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] - + 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 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: + if self.WITH_DEPENDS == True: self.run_with_dependents(op, filtered_ifacenames) - - 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 + else: + self.run_without_dependents(op, filtered_ifacenames) # Update persistant iface states try: @@ -730,7 +727,6 @@ class ifupdownMain(): traceback.print_tb(t) self.logger.warning('error saving state (%s)' %str(e)) - def up(self, auto=False, allow=None, ifacenames=None, excludepats=None, printdependency=None): return self.run('up', auto, allow, ifacenames, @@ -781,6 +777,8 @@ class ifupdownMain(): if op == 'query': return self.print_ifaceobjs_raw(filtered_ifacenames) + elif op == 'query-pretty': + return self.print_ifaceobjs_pretty(filtered_ifacenames) elif op == 'query-presumed': return self.print_ifaceobjs_saved_state_pretty( filtered_ifacenames) @@ -792,10 +790,10 @@ class ifupdownMain(): self.print_dependency(op, filtered_ifacenames, printdependency) return - if self.NODEPENDS == True: - self.run_without_dependents(op, filtered_ifacenames) - else: + if self.WITH_DEPENDS == True: self.run_with_dependents(op, filtered_ifacenames) + else: + self.run_without_dependents(op, filtered_ifacenames) if op == 'query-checkcurr': ret = self.print_ifaceobjscurr_pretty(filtered_ifacenames) @@ -845,7 +843,7 @@ class ifupdownMain(): if ifacedownlist is not None and len(ifacedownlist) > 0: self.logger.debug('bringing down interfaces: %s' %str(ifacedownlist)) - if self.NODEPENDS == True: + if self.WITH_DEPENDS == True: self.run_without_dependents('down', ifacedownlist) else: self.run_with_dependents('down', ifacedownlist) @@ -871,7 +869,7 @@ class ifupdownMain(): self.logger.debug('bringing up interfaces: %s' %str(filtered_ifacenames)) - if self.NODEPENDS == True: + if self.WITH_DEPENDS == True: self.run_without_dependents('up', filtered_ifacenames) else: self.run_with_dependents('up', filtered_ifacenames) @@ -908,10 +906,8 @@ class ifupdownMain(): def print_ifaceobjs_pretty(self, ifacenames): for i in ifacenames: - ifaceobjs = self.get_iface_objs(i) - for i in ifaceobjs: - i.dump_pretty(self.logger) - print '\n' + [ j.dump_pretty(self.logger) + for j in self.get_iface_objs(i)] def dump_ifaceobjs(self, ifacenames): for i in ifacenames: @@ -931,19 +927,24 @@ class ifupdownMain(): ifaceobj = self.get_ifaceobjcurr(i) if ifaceobj is None: continue if ifaceobj.get_status() == ifaceStatus.NOTFOUND: - print 'iface %s' %ifaceobj.get_name() + ' (not found)' + print 'iface %s' %ifaceobj.get_name() + ' (not found)\n' ret = 1 continue elif ifaceobj.get_status() == ifaceStatus.ERROR: ret = 1 + + if ifaceobj.is_config_present() == False: + continue + if format is None or format == 'nwifaces': ifaceobj.dump_pretty(self.logger) 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) + if self.ALL == False or self.WITH_DEPENDS: + dlist = ifaceobj.get_dependents() + if dlist is None or len(dlist) == 0: continue + self.print_ifaceobjscurr_pretty(dlist, format) return ret @@ -951,7 +952,10 @@ class ifupdownMain(): 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)' + print 'iface %s' %ifaceobj.get_name() + ' (not found)\n' + continue + + if ifaceobj.is_config_present() == False: continue if format is None or format == 'nwifaces': @@ -959,9 +963,10 @@ class ifupdownMain(): 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) + if self.ALL == False or self.WITH_DEPENDS: + 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): diff --git a/pkg/scheduler.py b/pkg/scheduler.py index d768b6d..7aa257e 100644 --- a/pkg/scheduler.py +++ b/pkg/scheduler.py @@ -202,9 +202,9 @@ class ifaceScheduler(ifupdownBase): try: self.logger.debug('calling topological sort on the graph ...') - sorted_ifacenames = graph.topological_sort(dependency_graph, - indegrees) - except Exception, e: + sorted_ifacenames = graph.topological_sort_graphs( + dependency_graph, indegrees) + except Exception: raise self.logger.debug('sorted iface list = %s' %sorted_ifacenames) diff --git a/sbin/ifupdown b/sbin/ifupdown index 142a71c..d922489 100755 --- a/sbin/ifupdown +++ b/sbin/ifupdown @@ -23,13 +23,13 @@ def run(args, op): logger.debug('creating ifupdown object ..') if op == 'up' or op == 'down' or op == 'reload': ifupdown_handle = ifupdownMain(force=args.force, - nodepends=args.nodepends, + withdepends=args.withdepends, perfmode=args.perfmode, njobs=args.jobs, dryrun=args.noact, cache=cachearg) elif op == 'query': - ifupdown_handle = ifupdownMain(nodepends=args.nodepends, + ifupdown_handle = ifupdownMain(withdepends=args.withdepends, perfmode=args.perfmode, njobs=args.jobs, format=args.format, @@ -56,6 +56,8 @@ def run(args, op): if os.path.isdir('/sys/class/net/%s' %i) == True] print iflist qtype='query-running' + elif args.pretty == True: + qtype='query-pretty' else: qtype='query' @@ -93,7 +95,9 @@ def deinit(): {} def update_argparser(argparser): - """ base parser """ + """ base parser, common to all commands """ + argparser.add_argument('-a', '--all', action='store_true', + help='process all interfaces marked \"auto\"') argparser.add_argument('iflist', metavar='IFACE', nargs='*', help='interfaces list') argparser.add_argument('-v', '--verbose', dest='verbose', @@ -106,17 +110,14 @@ def update_argparser(argparser): help=argparse.SUPPRESS) argparser.add_argument('--allow', dest='CLASS', help='ignore non-\"allow-CLASS\" interfaces') - argparser.add_argument('--nodepends', dest='nodepends', - action='store_true', help=argparse.SUPPRESS) + argparser.add_argument('--with-depends', dest='withdepends', + action='store_true', help='run with all dependencies') 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('--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 ' @@ -124,14 +125,16 @@ def update_argparser(argparser): def update_ifupdown_argparser(argparser): - argparser.add_argument('-a', '--all', action='store_true', - help='process all interfaces marked \"auto\"') + """ common arg parser for ifup and ifdown """ argparser.add_argument('-f', '--force', dest='force', action='store_true', help='force run all operations') argparser.add_argument('-n', '--no-act', dest='noact', action='store_true', help='print out what would happen,' + 'but don\'t do it') + argparser.add_argument('--print-dependency', + dest='printdependency', choices=['list', 'dot'], + help='print iface dependency') def update_ifup_argparser(argparser): update_ifupdown_argparser(argparser) @@ -140,8 +143,11 @@ def update_ifdown_argparser(argparser): update_ifupdown_argparser(argparser) def update_ifquery_argparser(argparser): + """ arg parser for ifquery options """ + + # -l is same as '-a', only hear for backward compatibility argparser.add_argument('-l', '--list', action='store_true', dest='all', - help='process all interfaces marked \"auto\"') + help=argparse.SUPPRESS) group = argparser.add_mutually_exclusive_group(required=False) group.add_argument('-r', '--running-state', dest='runningstate', action='store_true', @@ -149,7 +155,7 @@ def update_ifquery_argparser(argparser): group.add_argument('-c', '--check-state', dest='checkcurstate', action='store_true', - help='check running state of an interface') + help='check running against config of an interface') # presumed-state is state maintained by ifupdown group.add_argument('--presumed-state', dest='presumedstate', @@ -164,11 +170,17 @@ def update_ifquery_argparser(argparser): group.add_argument('--format', dest='format', default='nwifaces', choices=['nwifaces', 'json'], help=argparse.SUPPRESS) + group.add_argument('--pretty', action='store_true', dest='pretty', + help='pretty print config file entries') + def update_ifreload_argparser(argparser): update_ifupdown_argparser(argparser) def parse_args(argsv, op): - descr = 'interface management' + if op == 'query': + descr = 'interface query' + else: + descr = 'interface management' argparser = argparse.ArgumentParser(description=descr) update_argparser(argparser)