From 37c0543d349f3992709210739b60b8415d7f5e9d Mon Sep 17 00:00:00 2001 From: roopa Date: Thu, 30 Jan 2014 22:36:41 -0800 Subject: [PATCH] More fixes and changes Ticket: CM-1438 Reviewed By: Testing Done: unit tested with all kinds of interfaces some high level changes - moved ipv4/ipv6 address handling in a single module. dhcp into a separate module. - new link 'up' module - igmp fixes - many other fixes --- init.d/networking | 38 ++- pkg/gvgen.py | 615 +++++++++++++++++++++++++++++++++++++++ pkg/iface.py | 25 ++ pkg/ifupdownmain.py | 461 +++++++++++++++++------------ pkg/networkinterfaces.py | 19 +- pkg/scheduler.py | 71 +++-- sbin/ifupdown | 49 +++- 7 files changed, 1035 insertions(+), 243 deletions(-) create mode 100644 pkg/gvgen.py diff --git a/init.d/networking b/init.d/networking index b354d48..92ddf9d 100644 --- a/init.d/networking +++ b/init.d/networking @@ -28,6 +28,7 @@ verbose= [ "$VERBOSE" = yes ] && verbose=-v + process_exclusions() { set -- $EXCLUDE_INTERFACES exclusions="" @@ -94,12 +95,20 @@ ifup_hotplug () { fi } +ifupdown_init() { + [ ! -e /run/network ] && mkdir -p /run/network &>/dev/null + [ ! -e /etc/network/run ] && \ + ln -sf /run/network /etc/network/run &>/dev/null +} + case "$1" in start) if init_is_upstart; then exit 1 fi + ifupdown_init + if [ "$CONFIGURE_INTERFACES" = no ] then log_action_msg "Not configuring network interfaces, see /etc/default/networking" @@ -108,7 +117,7 @@ start) set -f exclusions=$(process_exclusions) log_action_begin_msg "Configuring network interfaces" - if ifup -a $verbose --perfmode + if ifup -a $verbose --perfmode --force then log_action_end_msg $? else @@ -120,6 +129,8 @@ stop) if init_is_upstart; then exit 0 fi + + ifupdown_init check_network_file_systems check_network_swap @@ -133,22 +144,37 @@ stop) reload) + ifupdown_init + log_action_begin_msg "Reloading network interfaces configuration" + + if ifreload -a + then + log_action_end_msg $? + else + log_action_end_msg $? + fi + ;; + +force-reload) + + ifupdown_init + log_action_begin_msg "Reloading network interfaces configuration" - state=$(cat /run/network/ifstate) - ifdown -a --exclude=lo $verbose --perfmode --force || true - if ifup --exclude=lo $state $verbose ; then + if ifreload -a --force + then log_action_end_msg $? else log_action_end_msg $? fi ;; -force-reload|restart) +restart) if init_is_upstart; then exit 1 fi - #log_warning_msg "Running $0 $1 is deprecated because it may not re-enable some interfaces" + ifupdown_init + log_action_begin_msg "Reconfiguring network interfaces" ifdown -a --exclude=lo $verbose --perfmode --force || true set -f diff --git a/pkg/gvgen.py b/pkg/gvgen.py new file mode 100644 index 0000000..f5d2607 --- /dev/null +++ b/pkg/gvgen.py @@ -0,0 +1,615 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# $Id: gvgen.py 13085 2008-02-25 16:11:50Z toady $ +""" +GvGen - Generate dot file to be processed by graphviz +Copyright (c) 2007-2008 INL +Written by Sebastien Tricaud + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +""" + +from sys import stdout + +gvgen_version = "0.9" + +debug = 0 +debug_tree_unroll = 0 + +class GvGen: + """ + Graphviz dot language Generation Class + For example of usage, please see the __main__ function + """ + + def __init__(self, legend_name=None, options="compound=true;"): # allow links between clusters + self.max_line_width = 10 + self.max_arrow_width = 2 + self.line_factor = 1 + self.arrow_factor = 0.5 + self.initial_line_width = 1.2 + self.initial_arrow_width = 0.8 + + self.options = options + + self.__id = 0 + self.__nodes = [] + self.__links = [] + self.__browse_level = 0 # Stupid depth level for self.browse + self.__opened_braces = [] # We count opened clusters + self.fd=stdout # File descriptor to output dot + self.padding_str=" " # Left padding to make children and parent look nice + self.__styles = {} + self.__default_style = [] + self.smart_mode = 0 # Disabled by default + + # The graph has a legend + if legend_name: + self.options = self.options + "rankdir=LR;" + self.legend = self.newItem(legend_name) + + def __node_new(self, name, parent=None, distinct=None): + """ + Create a new node in the data structure + @name: Name of the node, that will be the graphviz label + @parent: The node parent + @distinct: if true, will not create and node that has the same name + + Returns: The node created + """ + + # We first check for distincts + if distinct: + if self.__nodes: + for e in self.__nodes: + props = e['properties'] + if props['label'] == name: + # We found the label name matching, we return -1 + return -1 + + # We now insert into gvgen datastructure + self.__id += 1 + node = {'id': self.__id, # Internal ID + 'lock': 0, # When the node is written, it is locked to avoid further references + 'parent': parent, # Node parent for easy graphviz clusters + 'style':None, # Style that GvGen allow you to create + 'properties': { # Custom graphviz properties you can add, which will overide previously defined styles + 'label': name + } + } + + # Parents should be sorted first + if parent: + self.__nodes.insert(1, node) + else: + self.__nodes.append(node) + + return node + + def __link_smart(self, link): + """ + Creates a smart link if smart_mode activated: + if a -> b exists, and we now add a <- b, + instead of doing: a -> b + <- + we do: a <-> b + """ + + linkfrom = self.__link_exists(link['from_node'], link['to_node']) + linkto = self.__link_exists(link['to_node'], link['from_node']) + + if self.smart_mode: + if linkto: + self.__links.remove(linkto) + self.propertyAppend(link, "dir", "both") + + pw = self.propertyGet(linkfrom, "penwidth") + if pw: + pw = float(pw) + pw += self.line_factor + if pw < self.max_line_width: + self.propertyAppend(linkfrom, "penwidth", str(pw)) + else: + self.propertyAppend(link, "penwidth", str(self.initial_line_width)) + + aw = self.propertyGet(linkfrom, "arrowsize") + if aw: + aw = float(aw) + if aw < self.max_arrow_width: + aw += self.arrow_factor + self.propertyAppend(linkfrom, "arrowsize", str(aw)) + else: + self.propertyAppend(link, "arrowsize", str(self.initial_arrow_width)) + + if not linkfrom: + self.__links.append(link) + + + def __link_new(self, from_node, to_node, label = None, cl_from_node=None, cl_to_node=None): + """ + Creates a link between two nodes + @from_node: The node the link comes from + @to_node: The node the link goes to + + Returns: The link created + """ + + link = {'from_node': from_node, + 'to_node': to_node, + 'style':None, # Style that GvGen allow you to create + 'properties': {}, # Custom graphviz properties you can add, which will overide previously defined styles + 'cl_from_node':None, # When linking from a cluster, the link appears from this node + 'cl_to_node':None, # When linking to a cluster, the link appears to go to this node + } + + if label: + link['properties']['label'] = label + + if cl_from_node: + link['cl_from_node'] = cl_from_node + if cl_to_node: + link['cl_to_node'] = cl_to_node + + # We let smart link work for us + self.__link_smart(link) + + return link + + def __link_exists(self, from_node, to_node): + """ + Find if a link exists + @from_node: The node the link comes from + @to_node: The node the link goes to + + Returns: true if the given link already exists + """ + + for link in self.__links: + if link['from_node'] == from_node and link['to_node'] == to_node: + return link + + return None + + + def __has_children(self, parent): + """ + Find children to a given parent + Returns the children list + """ + children_list = [] + for e in self.__nodes: + if e['parent'] == parent: + children_list.append(e) + + return children_list + + def newItem(self, name, parent=None, distinct=None): + node = self.__node_new(name, parent, distinct) + + return node + + def newLink(self, src, dst, label=None, cl_src=None, cl_dst=None): + """ + Link two existing nodes with each other + """ + + return self.__link_new(src, dst, label, cl_src, cl_dst) + + def debug(self): + for e in self.__nodes: + print "element = " + str(e['id']) + + def collectLeaves(self, parent): + """ + Collect every leaf sharing the same parent + """ + cl = [] + for e in self.__nodes: + if e['parent'] == parent: + cl.append(e) + + return cl + + def collectUnlockedLeaves(self, parent): + """ + Collect every leaf sharing the same parent + unless it is locked + """ + cl = [] + for e in self.__nodes: + if e['parent'] == parent: + if not e['lock']: + cl.append(e) + + return cl + + def lockNode(self, node): + node['lock'] = 1 + + # + # Start: styles management + # + def styleAppend(self, stylename, key, val): + if stylename not in self.__styles: + self.__styles[stylename] = [] + + self.__styles[stylename].append([key, val]) + + def styleApply(self, stylename, node_or_link): + node_or_link['style'] = stylename + + def styleDefaultAppend(self, key, val): + self.__default_style.append([key, val]) + + # + # End: styles management + # + + # + # Start: properties management + # + def propertiesAsStringGet(self, node, props): + """ + Get the properties string according to parent/children + props is the properties dictionnary + """ + + allProps = {} + + # + # Default style come first, they can then be overriden + # + if self.__default_style: + allProps.update(self.__default_style) + + # + # First, we build the styles + # + if node['style']: + stylename = node['style'] + allProps.update(self.__styles[stylename]) + + # + # Now we build the properties: + # remember they override styles + # + allProps.update(props) + + if self.__has_children(node): + propStringList = ["%s=\"%s\";\n" % (k, v) for k, v in allProps.iteritems()] + properties = ''.join(propStringList) + else: + if props: + propStringList = ["%s=\"%s\"" % (k, v) for k, v in allProps.iteritems()] + properties = '[' + ','.join(propStringList) + ']' + else: + properties = '' + + return properties + + def propertiesLinkAsStringGet(self, link): + has_props = 0 + + props = {} + + if link['style']: + stylename = link['style'] + + # Build the properties string for node + props.update(self.__styles[stylename]) + + props.update(link['properties']) + + properties = '' + if props: + properties += ','.join(["%s=\"%s\"" % (str(k),str(val)) for k, val in props.iteritems()]) + return properties + + def propertyForeachLinksAppend(self, node, key, val): + for l in self.__links: + if l['from_node'] == node: + props = l['properties'] + props[key] = val + + def propertyAppend(self, node_or_link, key, val): + """ + Append a property to the wanted node or link + mynode = newItem(\"blah\") + Ex. propertyAppend(mynode, \"color\", \"red\") + """ + props = node_or_link['properties'] + props[key] = val + + def propertyGet(self, node_or_link, key): + """ + Get the value of a given property + Ex. prop = propertyGet(node, \"color\") + """ + try: + props = node_or_link['properties'] + return props[key] + except: + return None + + def propertyRemove(self, node_or_link, key): + """ + Remove a property to the wanted node or link + mynode = newItem(\"blah\") + Ex. propertyRemove(mynode, \"color\") + """ + props = node_or_link['properties'] + del props[key] + + # + # End: Properties management + # + + # + # For a good legend, the graph must have + # rankdir=LR property set. + # + def legendAppend(self, legendstyle, legenddescr, labelin=None): + if labelin: + item = self.newItem(legenddescr, self.legend) + self.styleApply(legendstyle, item) + else: + style = self.newItem("", self.legend) + descr = self.newItem(legenddescr, self.legend) + self.styleApply(legendstyle, style) + link = self.newLink(style,descr) + self.propertyAppend(link, "dir", "none") + self.propertyAppend(link, "style", "invis") + self.propertyAppend(descr,"shape","plaintext") + + def tree_debug(self, level, node, children): + if children: + print "(level:%d) Eid:%d has children (%s)" % (level,node['id'],str(children)) + else: + print "Eid:"+str(node['id'])+" has no children" + + # + # Core function that outputs the data structure tree into dot language + # + def tree(self, level, node, children): + """ + Core function to output dot which sorts out parents and children + and do it in the right order + """ + if debug: + print "/* Grabed node = %s*/" % str(node['id']) + + if node['lock'] == 1: # The node is locked, nothing should be printed + if debug: + print "/* The node (%s) is locked */" % str(node['id']) + + if self.__opened_braces: + self.fd.write(level * self.padding_str) + self.fd.write("}\n") + self.__opened_braces.pop() + return + + props = node['properties'] + + if children: + node['lock'] = 1 + self.fd.write(level * self.padding_str) + self.fd.write(self.padding_str + "subgraph cluster%d {\n" % node['id']) + properties = self.propertiesAsStringGet(node, props) + self.fd.write(level * self.padding_str) + self.fd.write(self.padding_str + "%s" % properties) + self.__opened_braces.append([node,level]) + else: + # We grab appropriate properties + properties = self.propertiesAsStringGet(node, props) + + # We get the latest opened elements + if self.__opened_braces: + last_cluster,last_level = self.__opened_braces[-1] + else: + last_cluster = None + last_level = 0 + + if debug: + if node['parent']: + parent_str = str(node['parent']['id']) + else: + parent_str = 'None' + if last_cluster: + last_cluster_str = str(last_cluster['id']) + else: + last_cluster_str = 'None' + print "/* e[parent] = %s, last_cluster = %s, last_level = %d, opened_braces: %s */" % (parent_str, last_cluster_str,last_level,str(self.__opened_braces)) + + # Write children/parent with properties + if node['parent']: + if node['parent'] != last_cluster: + while node['parent'] < last_cluster: + last_cluster,last_level = self.__opened_braces[-1] + if node['parent'] == last_cluster: + last_level += 1 + # We browse any property to build a string + self.fd.write(last_level * self.padding_str) + self.fd.write(self.padding_str + "node%d %s;\n" % (node['id'], properties)) + node['lock'] = 1 + else: + self.fd.write(last_level * self.padding_str) + self.fd.write(self.padding_str + "}\n") + self.__opened_braces.pop() + else: + self.fd.write(level * self.padding_str) + self.fd.write(self.padding_str + "node%d %s;\n" % (node['id'], properties) ) + node['lock'] = 1 + cl = self.collectUnlockedLeaves(node['parent']) + for l in cl: + props = l['properties'] + properties = self.propertiesAsStringGet(l, props) + self.fd.write(last_level * self.padding_str) + self.fd.write(self.padding_str + self.padding_str + "node%d %s;\n" % (l['id'], properties)) + node['lock'] = 1 + self.lockNode(l) + + self.fd.write(level * self.padding_str + "}\n") + self.__opened_braces.pop() + + else: + self.fd.write(self.padding_str + "node%d %s;\n" % (node['id'], properties)) + node['lock'] = 1 + + + def browse(self, node, cb): + """ + Browse nodes in a tree and calls cb providing node parameters + """ + children = self.__has_children(node) + if children: + cb(self.__browse_level, node, str(children)) + for c in children: + self.__browse_level += 1 + self.browse(c, cb) + + else: + cb(self.__browse_level, node, None) + self.__browse_level = 0 +# if debug: +# print "This node is not a child: " + str(node) + + def dotLinks(self, node): + """ + Write links between nodes + """ + for l in self.__links: + if l['from_node'] == node: + # Check if we link form a cluster + children = self.__has_children(node) + if children: + if l['cl_from_node']: + src = l['cl_from_node']['id'] + else: + src = children[0]['id'] + cluster_src = node['id'] + else: + src = node['id'] + cluster_src = '' + + # Check if we link to a cluster + children = self.__has_children(l['to_node']) + if children: + if l['cl_to_node']: + dst = l['cl_to_node']['id'] + else: + dst = children[0]['id'] + cluster_dst = l['to_node']['id'] + else: + dst = l['to_node']['id'] + cluster_dst = '' + + self.fd.write("node%d->node%d" % (src, dst)) + + props = self.propertiesLinkAsStringGet(l) + + # Build new properties if we link from or to a cluster + if cluster_src: + if props: + props += ',' + props += "ltail=cluster%d" % cluster_src + if cluster_dst: + if props: + props += ',' + props += "lhead=cluster%d" % cluster_dst + + if props: + self.fd.write(" [%s]" % props) + + self.fd.write(";\n") + + def dot(self, name=None, fd=stdout): + """ + Translates the datastructure into dot + """ + try: + self.fd = fd + + self.fd.write("/* Generated by GvGen v.%s (http://software.inl.fr/trac/wiki/GvGen) */\n\n" % (gvgen_version)) + + if name is None: + self.fd.write("digraph G {\n") + else: + self.fd.write("digraph %s {\n" %name) + + if self.options: + self.fd.write(self.options+"\n") + + # We write parents and children in order + for e in self.__nodes: + if debug_tree_unroll: + self.browse(e, self.tree_debug) + else: + self.browse(e, self.tree) + + # We write the connection between nodes + for e in self.__nodes: + self.dotLinks(e) + + # We put all the nodes belonging to the parent + self.fd.write("}\n") + finally: + # Remove our reference to file descriptor + self.fd = None + + +if __name__ == "__main__": + graph = GvGen() + + graph.smart_mode = 1 + + graph.styleDefaultAppend("color","blue") + + parents = graph.newItem("Parents") + father = graph.newItem("Bob", parents) + mother = graph.newItem("Alice", parents) + children = graph.newItem("Children") + child1 = graph.newItem("Carol", children) + child2 = graph.newItem("Eve", children) + child3 = graph.newItem("Isaac", children) + postman = graph.newItem("Postman") + graph.newLink(father,child1) + graph.newLink(child1, father) + graph.newLink(father, child1) + graph.newLink(father,child2) + graph.newLink(mother,child2) + myl = graph.newLink(mother,child1) + graph.newLink(mother,child3) + graph.newLink(postman,child3,"Email is safer") + graph.newLink(parents, postman) # Cluster link + + graph.propertyForeachLinksAppend(parents, "color", "blue") + + graph.propertyForeachLinksAppend(father, "label", "My big link") + graph.propertyForeachLinksAppend(father, "color", "red") + + graph.propertyAppend(postman, "color", "red") + graph.propertyAppend(postman, "fontcolor", "white") + + graph.styleAppend("link", "label", "mylink") + graph.styleAppend("link", "color", "green") + graph.styleApply("link", myl) + graph.propertyAppend(myl, "arrowhead", "empty") + + graph.styleAppend("Post", "color", "blue") + graph.styleAppend("Post", "style", "filled") + graph.styleAppend("Post", "shape", "rectangle") + graph.styleApply("Post", postman) + + + graph.dot() diff --git a/pkg/iface.py b/pkg/iface.py index d137b6a..98cd587 100644 --- a/pkg/iface.py +++ b/pkg/iface.py @@ -96,6 +96,8 @@ class iface(): AUTO = 0x1 HOT_PLUG = 0x2 + version = '0.1' + def __init__(self): self.name = None @@ -106,6 +108,7 @@ class iface(): self.state = ifaceState.NEW self.status = ifaceStatus.UNKNOWN self.flags = 0x0 + self.priv_flags = 0x0 self.refcnt = 0 # dependents that are listed as in the # config file @@ -157,6 +160,12 @@ class iface(): return self.config def is_config_present(self): + addr_method = self.get_addr_method() + if addr_method is not None: + if (addr_method.find('dhcp') != -1 or + addr_method.find('dhcp6') != -1): + return True + if self.config is None: return False @@ -320,6 +329,22 @@ class iface(): attr_status_str = ' (error)' self.config[attr_name] = attr_value + attr_status_str """ + def is_different(self, dstiface): + if self.name != dstiface.name: return True + if self.addr_family != dstiface.addr_family: return True + if self.addr_method != dstiface.addr_method: return True + if self.auto != dstiface.auto: return True + if self.classes != dstiface.classes: return True + + if any(True for k in self.config if k not in dstiface.config): + return True + + if any(True for k,v in self.config.items() + if v != dstiface.config.get(k)): return True + + + return False + def dump_raw(self, logger): indent = ' ' print (self.raw_lines[0]) diff --git a/pkg/ifupdownmain.py b/pkg/ifupdownmain.py index 92f9a98..8730e85 100644 --- a/pkg/ifupdownmain.py +++ b/pkg/ifupdownmain.py @@ -29,8 +29,13 @@ class ifupdownMain(): ALL = False STATE_CHECK = False - modules_dir='/etc/network' - builtin_modules_dir='/usr/share/ifupdownaddons' + # priv flags to mark iface objects + BUILTIN = 0x1 + NOCONFIG = 0x2 + + scripts_dir='/etc/network' + addon_modules_dir='/usr/share/ifupdownaddons' + addon_modules_configfile='/etc/network/.addons.conf' # iface dictionary in the below format: # { '' : [, ..] } @@ -50,19 +55,30 @@ class ifupdownMain(): # Dictionary representing operation, sub operation and modules # for every sub operation operations = { 'up' : - OrderedDict([('pre-up', OrderedDict({})), - ('up' , OrderedDict({})), - ('post-up' , OrderedDict({}))]), + OrderedDict([('pre-up', []), + ('up' , []), + ('post-up' , [])]), 'query-checkcurr' : - OrderedDict([('query-checkcurr', OrderedDict({}))]), + OrderedDict([('query-checkcurr', [])]), 'query-running' : - OrderedDict([('query-running', OrderedDict({}))]), + OrderedDict([('query-running', [])]), 'down' : - OrderedDict([('pre-down', OrderedDict({})), - ('down' , OrderedDict({})), - ('post-down' , OrderedDict({}))])} + OrderedDict([('pre-down', []), + ('down' , []), + ('post-down' , [])])} + + # For old style /etc/network/ bash scripts + operations_compat = { 'up' : + OrderedDict([('pre-up', []), + ('up' , []), + ('post-up' , [])]), + 'down' : + OrderedDict([('pre-down', []), + ('down' , []), + ('post-down' , [])])} + def __init__(self, force=False, dryrun=False, nowait=False, @@ -76,13 +92,16 @@ class ifupdownMain(): self.PERFMODE = perfmode self.WITH_DEPENDS = withdepends self.CACHE = cache - self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = True + self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False self.ifaces = OrderedDict() self.njobs = njobs self.pp = pprint.PrettyPrinter(indent=4) - self.load_modules_builtin(self.builtin_modules_dir) - self.load_modules(self.modules_dir) + self.modules = OrderedDict({}) + self.load_addon_modules_config() + self.load_addon_modules(self.addon_modules_dir) + self.load_scripts(self.scripts_dir) + self.dependency_graph = {} try: self.statemanager = stateManager() @@ -116,8 +135,6 @@ class ifupdownMain(): return self.FORCE def set_dryrun(self, dryrun): - if dryrun == True: - self.logger.debug('setting dryrun to true') self.DRYRUN = dryrun def get_dryrun(self): @@ -133,6 +150,12 @@ class ifupdownMain(): del self.ifaceobjdict self.ifaceobjdict = ifaceobjdict + def set_dependency_graph(self, dependency_graph): + self.dependency_graph = dependency_graph + + def get_dependency_graph(self): + return self.dependency_graph + def set_perfmode(self, perfmode): if perfmode == True: self.logger.debug('setting perfmode to true') @@ -196,7 +219,6 @@ class ifupdownMain(): def get_ifaceobjcurr(self, ifacename): return self.ifaceobjcurrdict.get(ifacename) - def get_ifaceobjrunning(self, ifacename): return self.ifaceobjrunningdict.get(ifacename) @@ -216,18 +238,21 @@ class ifupdownMain(): max = i.get_refcnt() return max - def create_n_save_ifaceobj(self, ifacename, increfcnt=False): + def create_n_save_ifaceobj(self, ifacename, priv_flags=None, + 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. """ ifaceobj = iface() ifaceobj.set_name(ifacename) + ifaceobj.priv_flags = priv_flags + ifaceobj.set_auto() if increfcnt == True: ifaceobj.inc_refcnt() self.ifaceobjdict[ifacename] = [ifaceobj] - def is_builtin_iface(self, ifacename): + def is_iface_builtin(self, ifacename): """ Returns true if iface name is a builtin interface. A builtin interface is an interface which ifupdown understands. @@ -238,6 +263,31 @@ class ifupdownMain(): return True return False + def is_ifaceobj_builtin(self, ifaceobj): + """ 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 (ifaceobj.priv_flags & self.BUILTIN) != 0: + return True + + return False + + def is_ifaceobj_noconfig(self, ifaceobj): + """ Returns true if iface name did not have a user defined config. + + These interfaces appear only when they are dependents of interfaces + which have user defined config + """ + + if (ifaceobj.priv_flags & self.NOCONFIG) != 0: + return True + + 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 @@ -254,15 +304,16 @@ class ifupdownMain(): 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_builtin_iface(d) == True or - self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG == False): - create_list.append(d) + if self.is_iface_builtin(d) == True: + self.create_n_save_ifaceobj(d, self.BUILTIN, True), + elif self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG == False: + # create fake devices to all dependents that dont + # have config + self.create_n_save_ifaceobj(d, self.NOCONFIG, True), else: del_list.append(d) else: @@ -272,48 +323,40 @@ 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 - self.logger.debug('%s: ' %ifaceobj.get_name() + 'getting dependency') - # Get dependents for interface by querying respective modules subopdict = self.operations.get(op) - for subop, mdict in subopdict.items(): - for mname, mdata in mdict.items(): - if mdata.get('ftype') == 'pmodule': - module = mdata.get('module') - 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, + for subop, mlist in subopdict.items(): + for mname in mlist: + module = self.modules.get(mname) + 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() + + if dlist is not None and len(dlist) > 0: + ifaceobj.set_realdev_dependents(dlist[:]) + self.logger.debug('%s: ' %ifaceobj.get_name() + 'got dependency list: %s' %str(dlist)) - break + break + return dlist - def generate_dependency_info(self, ifacenames, dependency_graph, op): + def populate_dependency_info(self, ifacenames, op): """ recursive function to generate iface dependency info """ - self.logger.debug('generating dependency info for %s' %str(ifacenames)) + if ifacenames is None: + ifacenames = self.ifaceobjdict.keys() + + self.logger.debug('populating dependency info for %s' %str(ifacenames)) iqueue = deque(ifacenames) while iqueue: @@ -333,11 +376,13 @@ class ifupdownMain(): if dlist is not None: self.preprocess_dependency_list(dlist, op) + self.logger.debug('%s: dependency list after processing: %s' + %(i, str(dlist))) ifaceobj.set_dependents(dlist) [iqueue.append(d) for d in dlist] - if dependency_graph.get(i) is None: - dependency_graph[i] = dlist + if self.dependency_graph.get(i) is None: + self.dependency_graph[i] = dlist def is_valid_state_transition(self, ifname, to_be_state): return self.statemanager.is_valid_state_transition(ifname, @@ -392,54 +437,56 @@ class ifupdownMain(): ' %s' %mname + ' of type %s' %mftype) - def load_modules_builtin(self, modules_dir): + def load_addon_modules_config(self): + with open(self.addon_modules_configfile, 'r') as f: + lines = f.readlines() + for l in lines: + litems = l.rstrip(' \n').split(',') + operation = litems[0] + mname = litems[1] + if operation.find('up') != -1: + self.operations['up'][operation].append(mname) + elif operation.find('down') != -1: + self.operations['down'][operation].append(mname) + + def load_addon_modules(self, modules_dir): """ load python modules from modules_dir Default modules_dir is /usr/share/ifupdownmodules """ - self.logger.info('loading builtin modules from %s' %modules_dir) if not modules_dir in sys.path: - sys.path.append(modules_dir) + sys.path.append(modules_dir) try: - module_list = os.listdir(modules_dir) - for module in module_list: - if re.search('.*\.pyc', module, 0) != None: - continue - - mname, mext = os.path.splitext(module) - if mext is not None and mext == '.py': - self.logger.info('loading ' + modules_dir + '/' + module) - try: - m = __import__(mname) - mclass = getattr(m, mname) - except: - raise - - minstance = mclass(force=self.get_force(), - dryrun=self.get_dryrun(), - nowait=self.get_nowait(), - perfmode=self.get_perfmode(), - cache=self.get_cache()) - ops = minstance.get_ops() - for op in ops: - 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) + for op, opdict in self.operations.items(): + for subop, mlist in opdict.items(): + for mname in mlist: + if self.modules.get(mname) is not None: + continue + mpath = modules_dir + '/' + mname + '.py' + if os.path.exists(mpath) == True: + try: + m = __import__(mname) + mclass = getattr(m, mname) + except: + raise + minstance = mclass(force=self.get_force(), + dryrun=self.get_dryrun(), + nowait=self.get_nowait(), + perfmode=self.get_perfmode(), + cache=self.get_cache()) + self.modules[mname] = minstance except: raise + # Assign all modules to query operations + self.operations['query-checkcurr']['query-checkcurr'] = self.modules.keys() + self.operations['query-running']['query-running'] = self.modules.keys() - def load_modules(self, modules_dir): + def load_scripts(self, modules_dir): """ loading user modules from /etc/network/. Note that previously loaded python modules override modules found @@ -447,45 +494,21 @@ 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 - + self.logger.info('looking for user scripts under %s' %modules_dir) + for op, subops in self.operations_compat.items(): for subop in subops.keys(): msubdir = modules_dir + '/if-%s.d' %subop - self.logger.info('loading modules under %s ...' %msubdir) + self.logger.info('loading scripts under %s ...' %msubdir) try: module_list = os.listdir(msubdir) for module in module_list: - if re.search('.*\.pyc', module, 0) != None: - continue - - mname, mext = os.path.splitext(module) - if mext is not None and mext == '.py': - self.logger.debug('loading ' + msubdir + '/' + module) - try: - m = imp.load_source(module, - msubdir + '/' + module) - mclass = getattr(m, mname) - except: - raise - minstance = mclass() - self.save_module(op, subop, mname, 'pmodule', - minstance) - else: - self.save_module(op, subop, mname, 'script', + if self.modules.get(module) is not None: + continue + self.operations_compat[op][subop].append( msubdir + '/' + module) except: raise - #self.logger.debug('modules ...') - #self.pp.pprint(self.operations) - - # For query, we add a special entry, basically use all 'up' modules - self.operations['query'] = self.operations.get('up') - - def conv_iface_namelist_to_objlist(self, intf_list): for intf in intf_list: iface_obj = self.get_iface(intf) @@ -498,83 +521,88 @@ class ifupdownMain(): def run_without_dependents(self, op, ifacenames): - runifacenames = [] - ifaceSched = ifaceScheduler(force=self.FORCE) + """ Run interfaces without executing their dependents. + + Even though we are running without dependents here, we will have + to cover the builtin dependents. Because the only way builtin + devices are operated on is when they are seen as dependents. + So we include them. And also we need to execute the user provided + interface names in order of their dependencies. + So, we created a special dependency_graph with interfaces matching + the above constraints here + + if self.ALL is True you are better off using the default + dependency graph self.dependency_graph that carries all dependents + """ + + if ifacenames == None: + raise ifupdownInvalidValue('no interfaces found') self.logger.debug('run_without_dependents for op %s' %op + ' for %s' %str(ifacenames)) - if ifacenames == None: - raise ifupdownInvalidValue('no interfaces found') + dependency_graph = {} + indegrees = {} + ifaceSched = ifaceScheduler(force=self.FORCE) - # 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 dependency_graph.get(i) is not None: + continue + + dependency_graph[i] = [] + indegrees[i] = 0 + ifaceobj = self.get_iface_obj_first(i) + dlist = ifaceobj.get_dependents() 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) + for d in dlist: + ifaceobj = self.get_iface_obj_first(d) + if (self.is_ifaceobj_builtin(ifaceobj) == True or + self.is_ifaceobj_noconfig(ifaceobj) == True or + d in ifacenames): + dependency_graph[i].append(d) + dependency_graph[d] = None + indegrees[d] = 1 + + self.logger.debug('dependency graph: %s' %str(dependency_graph)) + ifaceSched.run_iface_dependency_graph(self, dependency_graph, op, + indegrees, + graphsortall=True) def run_with_dependents(self, op, ifacenames): - dependency_graph = {} ret = 0 - self.logger.debug('run_with_dependents for op %s' - %op + ' for %s' %str(ifacenames)) + self.logger.debug('running \'%s\' with dependents for %s' + %(op, str(ifacenames))) ifaceSched = ifaceScheduler() - if ifacenames is None: ifacenames = self.ifaceobjdict.keys() - # generate dependency graph of interfaces - self.generate_dependency_info(ifacenames, dependency_graph, op) - if self.logger.isEnabledFor(logging.DEBUG) == True: self.logger.debug('dependency graph:') - self.pp.pprint(dependency_graph) + self.logger.debug(self.pp.pformat(self.dependency_graph)) if self.njobs > 1: ret = ifaceSched.run_iface_dependency_graph_parallel(self, - dependency_graph, op) + self.dependency_graph, op) else: ret = ifaceSched.run_iface_dependency_graph(self, - dependency_graph, op) + 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) + self.pp.pprint(self.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) + self.dependency_graph.keys()) + graph.generate_dots(self.dependency_graph, indegrees) def validate_ifaces(self, ifacenames): """ validates interface list for config existance. @@ -590,7 +618,7 @@ class ifupdownMain(): err_iface += ' ' + i if len(err_iface) != 0: - self.logger.error('did not find interfaces: %s' %err_iface) + self.logger.error('could not find interfaces: %s' %err_iface) return -1 return 0 @@ -657,7 +685,6 @@ class ifupdownMain(): """ main ifupdown run method """ if auto == True: - self.logger.debug('setting flag ALL') self.ALL = True self.WITH_DEPENDS = True @@ -706,6 +733,8 @@ class ifupdownMain(): raise Exception('no ifaces found matching ' + 'given allow lists') + self.populate_dependency_info(filtered_ifacenames, op) + if printdependency is not None: self.print_dependency(op, filtered_ifacenames, printdependency) return @@ -744,11 +773,12 @@ class ifupdownMain(): if auto == True: self.logger.debug('setting flag ALL') self.ALL = True + self.WITH_DEPENDS = 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) + map(lambda i: self.create_n_save_ifaceobj(i, self.NOCONFIG), + ifacenames) else: try: self.read_iface_config() @@ -786,9 +816,12 @@ class ifupdownMain(): return self.print_ifaceobjs_saved_state_detailed_pretty( filtered_ifacenames) - if printdependency is not None: - self.print_dependency(op, filtered_ifacenames, printdependency) - return + + self.populate_dependency_info(filtered_ifacenames, op) + + #if printdependency is not None: + # self.print_dependency(op, filtered_ifacenames, printdependency) + # return if self.WITH_DEPENDS == True: self.run_with_dependents(op, filtered_ifacenames) @@ -804,28 +837,36 @@ class ifupdownMain(): self.print_ifaceobjsrunning_pretty(filtered_ifacenames) return + def reload(self, auto=False, allow=None, - ifacenames=None, excludepats=None): + ifacenames=None, excludepats=None, downchangediface=False): """ main ifupdown run method """ allow_classes = [] self.logger.debug('reloading interface config ..') if auto == True: - self.logger.debug('setting flag ALL') self.ALL = True + self.WITH_DEPENDS = True try: + # Read the current interface config self.read_iface_config() except Exception, e: raise - # Save a copy of new iface objects + # generate dependency graph of interfaces + self.populate_dependency_info(ifacenames, 'up') + + # Save a copy of new iface objects and dependency_graph new_ifaceobjdict = self.get_ifaceobjdict() + new_dependency_graph = self.get_dependency_graph() 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 + # old interface config is read into self.ifaceobjdict + # self.read_old_iface_config() op = 'reload' else: @@ -833,20 +874,67 @@ class ifupdownMain(): 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())) + + # Generate the interface down list + # Interfaces that go into the down list: + # - interfaces that were present in last config and are not + # present in the new config + # - interfaces that were changed between the last and current + # config + # + + ifacedownlist = [] + for ifname, lastifobjlist in self.ifaceobjdict.items(): + objidx = 0 + + # If interface is not present in the new file + # append it to the down list + newifobjlist = new_ifaceobjdict.get(ifname) + if newifobjlist == None: + ifacedownlist.append(ifname) + continue + + if downchangediface == False: + continue + + # If interface has changed between the current file + # and the last installed append it to the down list + if len(newifobjlist) != len(lastifobjlist): + ifacedownlist.append(ifname) + continue + + # compare object list + for objidx in range(0, len(lastifobjlist)): + oldobj = lastifobjlist[objidx] + newobj = newifobjlist[objidx] + if newobj.is_different(oldobj) == True: + ifacedownlist.append(ifname) + continue + + + #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' + self.logger.info('Executing down on interfaces: %s' %str(ifacedownlist)) - if self.WITH_DEPENDS == True: - self.run_without_dependents('down', ifacedownlist) - else: + + # Generate dependency info for old config + self.populate_dependency_info(ifacedownlist, 'down') + + if len(ifacedownlist) == len(self.ifaceobjdict): + # if you are downing all interfaces, its better run + # with dependents self.run_with_dependents('down', ifacedownlist) + else: + # if not, down only the interfaces that we have in the + # down list + self.run_without_dependents('down', ifacedownlist) # Update persistant iface states try: @@ -859,20 +947,24 @@ class ifupdownMain(): t = sys.exc_info()[2] traceback.print_tb(t) self.logger.warning('error saving state (%s)' %str(e)) + else: + self.logger.debug('no interfaces to down ..') - # Now, run up with new dict + # Now, run up with new config dict self.set_ifaceobjdict(new_ifaceobjdict) + self.set_dependency_graph(new_dependency_graph) + 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)) + self.logger.info('Executing up on interfaces: %s' + %str(filtered_ifacenames)) if self.WITH_DEPENDS == True: - self.run_without_dependents('up', filtered_ifacenames) - else: self.run_with_dependents('up', filtered_ifacenames) + else: + self.run_without_dependents('up', filtered_ifacenames) # Update persistant iface states try: @@ -933,7 +1025,8 @@ class ifupdownMain(): elif ifaceobj.get_status() == ifaceStatus.ERROR: ret = 1 - if ifaceobj.is_config_present() == False: + if (self.is_iface_builtin(i) or + ifaceobj.is_config_present() == False): continue if format is None or format == 'nwifaces': @@ -955,6 +1048,8 @@ class ifupdownMain(): print 'iface %s' %ifaceobj.get_name() + ' (not found)\n' continue + #if (self.is_iface_builtin(i) and + # ifaceobj.is_config_present() == False): if ifaceobj.is_config_present() == False: continue diff --git a/pkg/networkinterfaces.py b/pkg/networkinterfaces.py index ef636b1..b4e591f 100644 --- a/pkg/networkinterfaces.py +++ b/pkg/networkinterfaces.py @@ -65,12 +65,10 @@ class networkInterfaces(): def process_source(self, lines, cur_idx, lineno): # Support regex - self.logger.debug('process_source ..%s' %lines[cur_idx]) + self.logger.debug('processing sourced line ..\'%s\'' %lines[cur_idx]) sourced_file = lines[cur_idx].split(' ', 2)[1] if sourced_file is not None: - self.logger.debug('process_source ..%s' %sourced_file) for f in glob.glob(sourced_file): - self.logger.info('Reading sourced file %s' %f) self.read_file(f) else: self.logger.warn('unable to read source line at %d', lineno) @@ -129,7 +127,11 @@ class networkInterfaces(): lines_consumed = line_idx - cur_idx # Create iface object - ifaceobj.set_name(ifacename) + if ifacename.find(':') != -1: + ifaceobj.set_name(ifacename.split(':')[0]) + else: + ifaceobj.set_name(ifacename) + ifaceobj.set_config(iface_config) ifaceobj.generate_env() if len(iface_attrs) > 2: @@ -146,7 +148,7 @@ class networkInterfaces(): ifaceobj.set_class(c) # Call iface found callback - self.logger.debug('saving interface %s' %ifaceobj.get_name()) + #self.logger.debug('saving interface %s' %ifaceobj.get_name()) self.callbacks.get('iface_found')(ifaceobj) return lines_consumed # Return next index @@ -214,8 +216,8 @@ class networkInterfaces(): try: from mako.template import Template except: - self.logger.warning('template engine mako not found ' + - 'skipping template parsing'); + self.logger.warning('template engine mako not found. ' + + 'skip template parsing ..'); return textdata t = Template(text=textdata, output_encoding='utf-8') @@ -226,7 +228,7 @@ class networkInterfaces(): if ifaces_file == None: ifaces_file=self.ifaces_file - self.logger.debug('reading ifaces_file %s' %ifaces_file) + self.logger.debug('reading interfaces file %s' %ifaces_file) f = open(ifaces_file) filedata = f.read() f.close() @@ -241,5 +243,4 @@ class networkInterfaces(): def load(self, filename=None): - self.logger.debug('loading ifaces file ..') return self.read_file(filename) diff --git a/pkg/scheduler.py b/pkg/scheduler.py index 7aa257e..ad77fca 100644 --- a/pkg/scheduler.py +++ b/pkg/scheduler.py @@ -35,32 +35,30 @@ class ifaceScheduler(ifupdownBase): self.__class__.__name__) self.FORCE = force - def run_iface_subop(self, ifupdownobj, ifaceobj, op, subop, mdict, cenv): + def run_iface_subop(self, ifupdownobj, ifaceobj, op, subop, mlist, cenv): """ Runs sub operation on an interface """ self.logger.debug('%s: ' %ifaceobj.get_name() + 'op %s' %op + ' subop = %s' %subop) - for mname, mdata in mdict.items(): - m = mdata.get('module') + for mname in mlist: + m = ifupdownobj.modules.get(mname) err = 0 try: - if (mdata.get('ftype') == 'pmodule' and - hasattr(m, 'run') == True): - self.logger.debug('%s: ' %ifaceobj.get_name() + - 'running module %s' %mname + - ' op %s' %op + ' subop %s' %subop) + if hasattr(m, 'run') == True: + self.logger.debug('%s: %s : running module %s' + %(ifaceobj.get_name(), subop, mname)) if op == 'query-checkcurr': - m.run(ifaceobj, subop, query_check=True, + # Dont check state if the interface object was + # auto generated + if ((ifaceobj.priv_flags & ifupdownobj.BUILTIN) != 0 or + (ifaceobj.priv_flags & ifupdownobj.NOCONFIG) != 0): + continue + m.run(ifaceobj, subop, query_ifaceobj=ifupdownobj.create_ifaceobjcurr( ifaceobj)) else: m.run(ifaceobj, subop) - else: - self.logger.debug('%s: ' %ifaceobj.get_name() + - 'running script %s' %mname + - ' op %s' %op + ' subop %s' %subop) - self.exec_command(m, cmdenv=cenv) except Exception, e: err = 1 self.log_error(str(e)) @@ -75,6 +73,18 @@ class ifaceScheduler(ifupdownBase): ifaceState.from_str(subop), ifaceStatus.SUCCESS) + # execute /etc/network/ scripts + subop_dict = ifupdownobj.operations_compat.get(op) + if subop_dict is None: return + for mname in subop_dict.get(subop): + self.logger.debug('%s: %s : running script %s' + %(ifaceobj.get_name(), subop, mname)) + try: + self.exec_command(mname, cmdenv=cenv) + except Exception, e: + err = 1 + self.log_error(str(e)) + def run_iface_subops(self, ifupdownobj, ifaceobj, op): """ Runs all sub operations on an interface """ @@ -84,8 +94,8 @@ class ifaceScheduler(ifupdownBase): # Each sub operation has a module list subopdict = ifupdownobj.operations.get(op) - for subop, mdict in subopdict.items(): - self.run_iface_subop(ifupdownobj, ifaceobj, op, subop, mdict, cenv) + for subop, mlist in subopdict.items(): + self.run_iface_subop(ifupdownobj, ifaceobj, op, subop, mlist, cenv) def run_iface(self, ifupdownobj, ifacename, op): @@ -134,7 +144,8 @@ class ifaceScheduler(ifupdownBase): sorted_by_dependency=False): """ Runs interface list through sub operation handler. """ - self.logger.debug('running sub operation %s on all given interfaces' %op) + self.logger.debug('running sub operation %s on all given interfaces' + %subop) iface_run_queue = deque(ifacenames) for i in range(0, len(iface_run_queue)): if op == 'up': @@ -176,9 +187,6 @@ class ifaceScheduler(ifupdownBase): 'up' """ - self.logger.debug('run_iface_list_stages: running interface list for %s' - %op) - # Each sub operation has a module list subopdict = ifupdownobj.operations.get(op) for subop, mdict in subopdict.items(): @@ -186,24 +194,29 @@ class ifaceScheduler(ifupdownBase): sorted_by_dependency) - def run_iface_dependency_graph(self, ifupdownobj, dependency_graph, - operation): + def run_iface_dependency_graph(self, ifupdownobj, dependency_graphs, + operation, indegrees=None, + graphsortall=False): """ runs interface dependency graph """ - indegrees = OrderedDict() - self.logger.debug('creating indegree array ...') - for ifacename in dependency_graph.keys(): - indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename) + if indegrees is None: + indegrees = OrderedDict() + for ifacename in dependency_graphs.keys(): + indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename) if self.logger.isEnabledFor(logging.DEBUG) == True: self.logger.debug('indegree array :') - ifupdownobj.pp.pprint(indegrees) + self.logger.debug(ifupdownobj.pp.pformat(indegrees)) try: self.logger.debug('calling topological sort on the graph ...') - sorted_ifacenames = graph.topological_sort_graphs( - dependency_graph, indegrees) + if graphsortall == True: + sorted_ifacenames = graph.topological_sort_graphs_all( + dependency_graphs, indegrees) + else: + sorted_ifacenames = graph.topological_sort_graphs( + dependency_graphs, indegrees) except Exception: raise diff --git a/sbin/ifupdown b/sbin/ifupdown index d922489..9fc4087 100755 --- a/sbin/ifupdown +++ b/sbin/ifupdown @@ -19,7 +19,11 @@ def run(args, op): if len(args.iflist) == 0: iflist = None - cachearg=False if iflist is not None or args.nocache == True else True + cachearg=(False if (iflist is not None or + args.nocache == True or + args.perfmode == True) + else True) + logger.debug('creating ifupdown object ..') if op == 'up' or op == 'down' or op == 'reload': ifupdown_handle = ifupdownMain(force=args.force, @@ -35,7 +39,7 @@ def run(args, op): format=args.format, cache=cachearg) - logger.debug('calling %s' %op + ' for all interfaces ..') + logger.debug('calling \'%s\'' %op + ' for all interfaces ..') if op == 'up': ifupdown_handle.up(args.all, args.CLASS, iflist, excludepats=args.excludepats, @@ -54,7 +58,6 @@ def run(args, op): 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' elif args.pretty == True: qtype='query-pretty' @@ -62,13 +65,14 @@ def run(args, op): qtype='query' ifupdown_handle.query(qtype, args.all, args.CLASS, iflist, - excludepats=args.excludepats) + excludepats=args.excludepats, + printdependency=args.printdependency) 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) + excludepats=args.excludepats, + downchangediface=args.downchangediface) except: raise @@ -96,10 +100,12 @@ def deinit(): def update_argparser(argparser): """ base parser, common to all commands """ - argparser.add_argument('-a', '--all', action='store_true', + + argparser.add_argument('-a', '--all', action='store_true', required=False, help='process all interfaces marked \"auto\"') argparser.add_argument('iflist', metavar='IFACE', - nargs='*', help='interfaces list') + nargs='*', help='interface list separated by spaces') + argparser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='verbose') argparser.add_argument('-d', '--debug', dest='debug', @@ -111,7 +117,7 @@ def update_argparser(argparser): argparser.add_argument('--allow', dest='CLASS', help='ignore non-\"allow-CLASS\" interfaces') argparser.add_argument('--with-depends', dest='withdepends', - action='store_true', help='run with all dependencies') + action='store_true', help='run with all dependent interfaces') argparser.add_argument('--perfmode', dest='perfmode', action='store_true', help=argparse.SUPPRESS) argparser.add_argument('-j', '--jobs', dest='jobs', type=int, @@ -119,10 +125,9 @@ def update_argparser(argparser): 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 by a PATTERN ' - + '(note that this option doesn\'t disable mappings)') - + action='append', + help='Exclude interfaces from the list of interfaces' + + ' to operate on') def update_ifupdown_argparser(argparser): """ common arg parser for ifup and ifdown """ @@ -145,7 +150,7 @@ def update_ifdown_argparser(argparser): def update_ifquery_argparser(argparser): """ arg parser for ifquery options """ - # -l is same as '-a', only hear for backward compatibility + # -l is same as '-a', only here for backward compatibility argparser.add_argument('-l', '--list', action='store_true', dest='all', help=argparse.SUPPRESS) group = argparser.add_mutually_exclusive_group(required=False) @@ -173,12 +178,20 @@ def update_ifquery_argparser(argparser): group.add_argument('--pretty', action='store_true', dest='pretty', help='pretty print config file entries') + argparser.add_argument('--print-dependency', + dest='printdependency', choices=['list', 'dot'], + help=argparse.SUPPRESS) + def update_ifreload_argparser(argparser): update_ifupdown_argparser(argparser) + argparser.add_argument('--down-changediface', dest='downchangediface', + action='store_true', + help='down interfaces that have changed before bringing them up') + def parse_args(argsv, op): if op == 'query': - descr = 'interface query' + descr = 'query interfaces (all or interface list)' else: descr = 'interface management' @@ -214,8 +227,12 @@ def main(argv): # Command line arg parser args = parse_args(argv[1:], op) + if len(args.iflist) == 0 and args.all == False: + print '\'-a\' option or interface list are required' + exit(1) + if len(args.iflist) > 0 and args.all is True: - print 'interface list cannot be specified with all option' + print '\'-a\' option and interface list are mutually exclusive' exit(1) init(args) -- 2.39.2