3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
16 from ifupdown2
.ifupdown
.graph
import *
17 from ifupdown2
.ifupdown
.ifupdownbase
import *
19 from ifupdown2
.ifupdown
.iface
import *
20 from ifupdown2
.ifupdown
.utils
import utils
21 from ifupdown2
.ifupdown
.statemanager
import *
23 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
25 from ifupdown
.graph
import *
26 from ifupdown
.ifupdownbase
import *
28 from ifupdown
.iface
import *
29 from ifupdown
.utils
import utils
30 from ifupdown
.statemanager
import *
32 import ifupdown
.ifupdownflags
as ifupdownflags
35 class ifaceSchedulerFlags():
36 """ Enumerates scheduler flags """
41 class ifaceScheduler():
42 """ scheduler functions to schedule configuration of interfaces.
44 supports scheduling of interfaces serially in plain interface list
45 or dependency graph format.
55 cls
._STATE
_CHECK
= True
56 cls
._SCHED
_STATUS
= True
59 def get_sched_status(cls
):
60 return cls
._SCHED
_STATUS
63 def set_sched_status(cls
, state
):
64 cls
._SCHED
_STATUS
= state
67 def run_iface_op(cls
, ifupdownobj
, ifaceobj
, op
, cenv
=None):
68 """ Runs sub operation on an interface """
69 ifacename
= ifaceobj
.name
71 if ifupdownobj
.type and ifupdownobj
.type != ifaceobj
.type:
74 if not ifupdownobj
.flags
.ADDONS_ENABLE
: return
75 if op
== 'query-checkcurr':
76 query_ifaceobj
=ifupdownobj
.create_n_save_ifaceobjcurr(ifaceobj
)
77 # If not type bridge vlan and the object does not exist,
78 # mark not found and return
79 if (not ifupdownobj
.link_exists(ifaceobj
.name
) and
80 ifaceobj
.type != ifaceType
.BRIDGE_VLAN
):
81 query_ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
84 for mname
in ifupdownobj
.module_ops
.get(op
):
85 m
= ifupdownobj
.modules
.get(mname
)
89 msg
= ('%s: %s : running module %s' %(ifacename
, op
, mname
))
90 if op
== 'query-checkcurr':
91 # Dont check curr if the interface object was
93 if (ifaceobj
.priv_flags
and
94 ifaceobj
.priv_flags
.NOCONFIG
):
96 ifupdownobj
.logger
.debug(msg
)
97 m
.run(ifaceobj
, op
, query_ifaceobj
,
98 ifaceobj_getfunc
=ifupdownobj
.get_ifaceobjs
)
100 ifupdownobj
.logger
.debug(msg
)
102 ifaceobj_getfunc
=ifupdownobj
.get_ifaceobjs
)
104 if not ifupdownobj
.ignore_error(str(e
)):
106 ifupdownobj
.logger
.error(str(e
))
107 # Continue with rest of the modules
110 if err
or ifaceobj
.status
== ifaceStatus
.ERROR
:
111 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
113 if 'up' in op
or 'down' in op
or 'query-checkcurr' in op
:
114 cls
.set_sched_status(False)
116 # Mark success only if the interface was not already
118 status
= (ifaceobj
.status
119 if ifaceobj
.status
== ifaceStatus
.ERROR
120 else ifaceStatus
.SUCCESS
)
121 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
124 if ifupdownobj
.config
.get('addon_scripts_support', '0') == '1':
125 # execute /etc/network/ scripts
126 os
.environ
['IFACE'] = ifaceobj
.name
if ifaceobj
.name
else ''
127 os
.environ
['LOGICAL'] = ifaceobj
.name
if ifaceobj
.name
else ''
128 os
.environ
['METHOD'] = ifaceobj
.addr_method
if ifaceobj
.addr_method
else ''
129 os
.environ
['ADDRFAM'] = ','.join(ifaceobj
.addr_family
) if ifaceobj
.addr_family
else ''
130 for mname
in ifupdownobj
.script_ops
.get(op
, []):
131 ifupdownobj
.logger
.debug('%s: %s : running script %s'
132 %(ifacename
, op
, mname
))
134 utils
.exec_command(mname
, env
=cenv
)
136 if "permission denied" in str(e
).lower():
137 ifupdownobj
.logger
.warning('%s: %s %s' % (ifacename
, op
, str(e
)))
139 ifupdownobj
.log_error('%s: %s %s' % (ifacename
, op
, str(e
)))
142 def run_iface_list_ops(cls
, ifupdownobj
, ifaceobjs
, ops
):
143 """ Runs all operations on a list of interface
144 configurations for the same interface
147 # minor optimization. If operation is 'down', proceed only
148 # if interface exists in the system
149 ifacename
= ifaceobjs
[0].name
150 ifupdownobj
.logger
.info('%s: running ops ...' %ifacename
)
152 if ('down' in ops
[0] and
153 ifaceobjs
[0].type != ifaceType
.BRIDGE_VLAN
and
154 ifaceobjs
[0].addr_method
!= 'ppp' and
155 not ifupdownobj
.link_exists(ifacename
)):
156 ifupdownobj
.logger
.debug('%s: does not exist' %ifacename
)
157 # run posthook before you get out of here, so that
158 # appropriate cleanup is done
159 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
161 for ifaceobj
in ifaceobjs
:
162 ifaceobj
.status
= ifaceStatus
.SUCCESS
163 posthookfunc(ifupdownobj
, ifaceobj
, 'down')
166 # first run ifupdownobj handlers. This is good enough
167 # for the first object in the list
168 handler
= ifupdownobj
.ops_handlers
.get(op
)
171 handler(ifupdownobj
, ifaceobjs
[0])
173 if not ifupdownobj
.link_master_slave_ignore_error(str(e
)):
174 ifupdownobj
.logger
.warn('%s: %s'
175 %(ifaceobjs
[0].name
, str(e
)))
177 for ifaceobj
in ifaceobjs
:
178 cls
.run_iface_op(ifupdownobj
, ifaceobj
, op
,
179 cenv
=ifupdownobj
.generate_running_env(ifaceobj
, op
)
180 if ifupdownobj
.config
.get('addon_scripts_support',
181 '0') == '1' else None)
182 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
185 [posthookfunc(ifupdownobj
, ifaceobj
, ops
[0])
186 for ifaceobj
in ifaceobjs
]
188 ifupdownobj
.logger
.warn('%s' %str
(e
))
192 def _check_upperifaces(cls
, ifupdownobj
, ifaceobj
, ops
, parent
,
193 followdependents
=False):
194 """ Check if upperifaces are hanging off us and help caller decide
195 if he can proceed with the ops on this device
197 Returns True or False indicating the caller to proceed with the
200 # proceed only for down operation
201 if 'down' not in ops
[0]:
204 if (ifupdownobj
.flags
.SCHED_SKIP_CHECK_UPPERIFACES
):
207 if (ifupdownflags
.flags
.FORCE
or
208 not ifupdownobj
.flags
.ADDONS_ENABLE
or
209 (not ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
) and
210 ifupdownobj
.config
.get('warn_on_ifdown', '0') == '0' and
211 not ifupdownflags
.flags
.ALL
)):
214 ulist
= ifaceobj
.upperifaces
218 # Get the list of upper ifaces other than the parent
219 tmpulist
= ([u
for u
in ulist
if u
!= parent
] if parent
223 # XXX: This is expensive. Find a cheaper way to do this.
224 # if any of the upperdevs are present,
225 # return false to the caller to skip this interface
227 if ifupdownobj
.link_exists(u
):
228 if not ifupdownflags
.flags
.ALL
:
229 if ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
):
230 ifupdownobj
.logger
.info('%s: skipping interface down,'
231 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
233 ifupdownobj
.logger
.warn('%s: skipping interface down,'
234 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
239 def run_iface_graph(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
240 order
=ifaceSchedulerFlags
.POSTORDER
,
241 followdependents
=True):
242 """ runs interface by traversing all nodes rooted at itself """
244 # Each ifacename can have a list of iface objects
245 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
247 raise Exception('%s: not found' %ifacename
)
249 # Check state of the dependent. If it is already brought up, return
250 if (cls
._STATE
_CHECK
and
251 (ifaceobjs
[0].state
== ifaceState
.from_str(ops
[-1]))):
252 ifupdownobj
.logger
.debug('%s: already processed' %ifacename
)
255 for ifaceobj
in ifaceobjs
:
256 if not cls
._check
_upperifaces
(ifupdownobj
, ifaceobj
,
257 ops
, parent
, followdependents
):
260 # If inorder, run the iface first and then its dependents
261 if order
== ifaceSchedulerFlags
.INORDER
:
262 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
264 for ifaceobj
in ifaceobjs
:
265 # Run lowerifaces or dependents
266 dlist
= ifaceobj
.lowerifaces
268 ifupdownobj
.logger
.debug('%s: found dependents %s'
269 %(ifacename
, str(dlist
)))
271 if not followdependents
:
272 # XXX: this is yet another extra step,
273 # but is needed for interfaces that are
274 # implicit dependents. even though we are asked to
275 # not follow dependents, we must follow the ones
276 # that dont have user given config. Because we own them
277 new_dlist
= [d
for d
in dlist
278 if ifupdownobj
.is_iface_noconfig(d
)]
280 cls
.run_iface_list(ifupdownobj
, new_dlist
, ops
,
281 ifacename
, order
, followdependents
,
282 continueonfailure
=False)
284 cls
.run_iface_list(ifupdownobj
, dlist
, ops
,
287 continueonfailure
=False)
289 if (ifupdownobj
.ignore_error(str(e
))):
292 # Dont bring the iface up if children did not come up
293 ifaceobj
.set_state_n_status(ifaceState
.NEW
,
296 if order
== ifaceSchedulerFlags
.POSTORDER
:
297 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
300 def run_iface_list(cls
, ifupdownobj
, ifacenames
,
301 ops
, parent
=None, order
=ifaceSchedulerFlags
.POSTORDER
,
302 followdependents
=True, continueonfailure
=True):
303 """ Runs interface list """
305 for ifacename
in ifacenames
:
307 cls
.run_iface_graph(ifupdownobj
, ifacename
, ops
, parent
,
308 order
, followdependents
)
310 if continueonfailure
:
311 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
312 traceback
.print_tb(sys
.exc_info()[2])
313 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
316 if (ifupdownobj
.ignore_error(str(e
))):
319 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
322 def run_iface_graph_upper(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
323 followdependents
=True, skip_root
=False):
324 """ runs interface by traversing all nodes rooted at itself """
326 # Each ifacename can have a list of iface objects
327 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
329 raise Exception('%s: not found' %ifacename
)
331 if (cls
._STATE
_CHECK
and
332 (ifaceobjs
[0].state
== ifaceState
.from_str(ops
[-1]))):
333 ifupdownobj
.logger
.debug('%s: already processed' %ifacename
)
337 # run the iface first and then its upperifaces
338 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
339 for ifaceobj
in ifaceobjs
:
341 ulist
= ifaceobj
.upperifaces
343 ifupdownobj
.logger
.debug('%s: found upperifaces %s'
344 %(ifacename
, str(ulist
)))
346 cls
.run_iface_list_upper(ifupdownobj
, ulist
, ops
,
349 continueonfailure
=True)
351 if (ifupdownobj
.ignore_error(str(e
))):
357 def run_iface_list_upper(cls
, ifupdownobj
, ifacenames
,
358 ops
, parent
=None, followdependents
=True,
359 continueonfailure
=True, skip_root
=False):
360 """ Runs interface list """
362 for ifacename
in ifacenames
:
364 cls
.run_iface_graph_upper(ifupdownobj
, ifacename
, ops
, parent
,
365 followdependents
, skip_root
)
367 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
368 traceback
.print_tb(sys
.exc_info()[2])
369 ifupdownobj
.logger
.warn('%s : %s' %(ifacename
, str(e
)))
373 def _get_valid_upperifaces(cls
, ifupdownobj
, ifacenames
,
375 """ Recursively find valid upperifaces
377 valid upperifaces are:
378 - An upperiface which had no user config (example builtin
379 interfaces. usually vlan interfaces.)
380 - or had config and previously up
381 - and interface currently does not exist
382 - or is a bridge (because if your upperiface was a bridge
383 - u will have to execute up on the bridge
384 to enslave the port and apply bridge attributes to the port) """
387 for ifacename
in ifacenames
:
389 ifaceobj
= ifupdownobj
.get_ifaceobj_first(ifacename
)
392 ulist
= Set(ifaceobj
.upperifaces
).difference(upperifacenames
)
395 uifaceobj
= ifupdownobj
.get_ifaceobj_first(u
)
398 has_config
= not (uifaceobj
.priv_flags
and
399 uifaceobj
.priv_flags
.NOCONFIG
)
400 if (((has_config
and ifupdownobj
.get_ifaceobjs_saved(u
)) or
401 not has_config
) and (not ifupdownobj
.link_exists(u
)
402 # Do this always for a bridge. Note that this is
403 # not done for a vlan aware bridge because,
404 # in the vlan aware bridge case, the bridge module
405 # applies the bridge port configuration on the port
406 # when up is scheduled on the port.
407 or (uifaceobj
.link_kind
== ifaceLinkKind
.BRIDGE
))):
409 upperifacenames
.extend(nulist
)
410 allupperifacenames
.extend(upperifacenames
)
412 cls
._get
_valid
_upperifaces
(ifupdownobj
, upperifacenames
,
417 def run_upperifaces(cls
, ifupdownobj
, ifacenames
, ops
,
418 continueonfailure
=True):
419 """ Run through valid upperifaces """
422 cls
._get
_valid
_upperifaces
(ifupdownobj
, ifacenames
, upperifaces
)
425 # dump valid upperifaces
426 ifupdownobj
.logger
.debug(upperifaces
)
427 for u
in upperifaces
:
429 ifaceobjs
= ifupdownobj
.get_ifaceobjs(u
)
432 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
434 if continueonfailure
:
435 ifupdownobj
.logger
.warn('%s' %str
(e
))
438 def _dump_dependency_info(cls
, ifupdownobj
, ifacenames
,
439 dependency_graph
=None, indegrees
=None):
440 ifupdownobj
.logger
.info('{\n')
441 ifupdownobj
.logger
.info('\nifaceobjs:')
442 for iname
in ifacenames
:
443 iobjs
= ifupdownobj
.get_ifaceobjs(iname
)
445 iobj
.dump(ifupdownobj
.logger
)
446 if (dependency_graph
):
447 ifupdownobj
.logger
.info('\nDependency Graph:')
448 ifupdownobj
.logger
.info(dependency_graph
)
450 ifupdownobj
.logger
.info('\nIndegrees:')
451 ifupdownobj
.logger
.info(indegrees
)
452 ifupdownobj
.logger
.info('}\n')
455 def get_sorted_iface_list(cls
, ifupdownobj
, ifacenames
, ops
,
456 dependency_graph
, indegrees
=None):
457 if len(ifacenames
) == 1:
459 # Get a sorted list of all interfaces
461 indegrees
= OrderedDict()
462 for ifacename
in dependency_graph
.keys():
463 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
465 #cls._dump_dependency_info(ifupdownobj, ifacenames,
466 # dependency_graph, indegrees)
468 ifacenames_all_sorted
= graph
.topological_sort_graphs_all(
469 dependency_graph
, indegrees
)
470 # if ALL was set, return all interfaces
471 if ifupdownflags
.flags
.ALL
:
472 return ifacenames_all_sorted
474 # else return ifacenames passed as argument in sorted order
475 ifacenames_sorted
= []
476 [ifacenames_sorted
.append(ifacename
)
477 for ifacename
in ifacenames_all_sorted
478 if ifacename
in ifacenames
]
479 return ifacenames_sorted
482 def sched_ifaces(cls
, ifupdownobj
, ifacenames
, ops
,
483 dependency_graph
=None, indegrees
=None,
484 order
=ifaceSchedulerFlags
.POSTORDER
,
485 followdependents
=True, skipupperifaces
=False, sort
=False):
486 """ runs interface configuration modules on interfaces passed as
487 argument. Runs topological sort on interface dependency graph.
490 **ifupdownobj** (object): ifupdownMain object
492 **ifacenames** (list): list of interface names
494 **ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
496 **dependency_graph** (dict): dependency graph in adjacency list format
499 **indegrees** (dict): indegree array of the dependency graph
501 **order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
503 **followdependents** (bool): follow dependent interfaces if true
505 **sort** (bool): sort ifacelist in the case where ALL is not set
510 # if ALL/auto interfaces are specified,
511 # - walk the dependency tree in postorder or inorder depending
513 # (This is to run interfaces correctly in order)
515 # - sort iface list if the ifaces belong to a "class"
516 # - else just run iface list in the order they were specified
518 # Run any upperifaces if available
520 followupperifaces
= False
522 skip_ifacesort
= int(ifupdownobj
.config
.get('skip_ifacesort', '0'))
523 if not skip_ifacesort
and not indegrees
:
524 indegrees
= OrderedDict()
525 for ifacename
in dependency_graph
.keys():
526 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
528 if not ifupdownflags
.flags
.ALL
:
530 # If there is any interface that does not exist, maybe it
531 # is a logical interface and we have to followupperifaces
532 # when it comes up, so lets get that list.
533 if any([True for i
in ifacenames
534 if ifupdownobj
.must_follow_upperifaces(i
)]):
535 followupperifaces
= (True if
536 [i
for i
in ifacenames
537 if not ifupdownobj
.link_exists(i
)]
539 # sort interfaces only if the caller asked to sort
540 # and skip_ifacesort is not on.
541 if not skip_ifacesort
and sort
:
542 run_queue
= cls
.get_sorted_iface_list(ifupdownobj
, ifacenames
,
543 ops
, dependency_graph
, indegrees
)
544 if run_queue
and 'up' in ops
[0]:
547 # if -a is set, we pick the interfaces
548 # that have no parents and use a sorted list of those
549 if not skip_ifacesort
:
550 sorted_ifacenames
= cls
.get_sorted_iface_list(ifupdownobj
,
551 ifacenames
, ops
, dependency_graph
,
553 if sorted_ifacenames
:
554 # pick interfaces that user asked
555 # and those that dont have any dependents first
556 [run_queue
.append(ifacename
)
557 for ifacename
in sorted_ifacenames
558 if ifacename
in ifacenames
and
559 not indegrees
.get(ifacename
)]
560 ifupdownobj
.logger
.debug('graph roots (interfaces that ' +
561 'dont have dependents):' + ' %s' %str
(run_queue
))
563 ifupdownobj
.logger
.warn('interface sort returned None')
565 # If queue not present, just run interfaces that were asked by the
568 run_queue
= list(ifacenames
)
569 # if we are taking the order of interfaces as specified
570 # in the interfaces file, we should reverse the list if we
571 # want to down. This can happen if 'skip_ifacesort'
577 cls
.run_iface_list(ifupdownobj
, run_queue
, ops
,
578 parent
=None, order
=order
,
579 followdependents
=followdependents
)
580 if not cls
.get_sched_status():
583 if (not skipupperifaces
and
584 ifupdownobj
.config
.get('skip_upperifaces', '0') == '0' and
585 ((not ifupdownflags
.flags
.ALL
and followdependents
) or
586 followupperifaces
) and
588 # If user had given a set of interfaces to bring up
589 # try and execute 'up' on the upperifaces
590 ifupdownobj
.logger
.info('running upperifaces (parent interfaces) ' +
593 # upperiface bring up is best effort.
594 # eg case: if we are bringing up a bridge port
595 # this section does an 'ifup on the bridge'
596 # so that the recently up'ed bridge port gets enslaved
597 # to the bridge. But the up on the bridge may
598 # throw out more errors if the bridge is not
599 # in the correct state. Lets not surprise
600 # the user with such errors when he has
601 # only requested to bring up the bridge port.
602 cls
._STATE
_CHECK
= False
603 ifupdownflags
.flags
.IGNORE_ERRORS
= True
604 cls
.run_upperifaces(ifupdownobj
, ifacenames
, ops
)
606 ifupdownflags
.flags
.IGNORE_ERRORS
= False
607 cls
._STATE
_CHECK
= True
608 # upperiface bringup is best effort, so dont propagate errors
609 # reset scheduler status to True
610 cls
.set_sched_status(True)