3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
15 from ifupdown2
.ifupdown
.graph
import *
16 from ifupdown2
.ifupdown
.ifupdownbase
import *
18 from ifupdown2
.ifupdown
.iface
import *
19 from ifupdown2
.ifupdown
.utils
import utils
20 from ifupdown2
.ifupdown
.statemanager
import *
22 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
24 from ifupdown
.graph
import *
25 from ifupdown
.ifupdownbase
import *
27 from ifupdown
.iface
import *
28 from ifupdown
.utils
import utils
29 from ifupdown
.statemanager
import *
31 import ifupdown
.ifupdownflags
as ifupdownflags
34 class ifaceSchedulerFlags():
35 """ Enumerates scheduler flags """
40 class ifaceScheduler():
41 """ scheduler functions to schedule configuration of interfaces.
43 supports scheduling of interfaces serially in plain interface list
44 or dependency graph format.
54 cls
._STATE
_CHECK
= True
55 cls
._SCHED
_STATUS
= True
58 def get_sched_status(cls
):
59 return cls
._SCHED
_STATUS
62 def set_sched_status(cls
, state
):
63 cls
._SCHED
_STATUS
= state
66 def run_iface_op(cls
, ifupdownobj
, ifaceobj
, op
, cenv
=None):
67 """ Runs sub operation on an interface """
68 ifacename
= ifaceobj
.name
70 if ifupdownobj
.type and ifupdownobj
.type != ifaceobj
.type:
73 if not ifupdownobj
.flags
.ADDONS_ENABLE
: return
74 if op
== 'query-checkcurr':
75 query_ifaceobj
=ifupdownobj
.create_n_save_ifaceobjcurr(ifaceobj
)
76 # If not type bridge vlan and the object does not exist,
77 # mark not found and return
78 if (not ifupdownobj
.link_exists(ifaceobj
.name
) and
79 ifaceobj
.type != ifaceType
.BRIDGE_VLAN
):
80 query_ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
83 for mname
in ifupdownobj
.module_ops
.get(op
):
84 m
= ifupdownobj
.modules
.get(mname
)
88 msg
= ('%s: %s : running module %s' %(ifacename
, op
, mname
))
89 if op
== 'query-checkcurr':
90 # Dont check curr if the interface object was
92 if (ifaceobj
.priv_flags
and
93 ifaceobj
.priv_flags
.NOCONFIG
):
95 ifupdownobj
.logger
.debug(msg
)
96 m
.run(ifaceobj
, op
, query_ifaceobj
,
97 ifaceobj_getfunc
=ifupdownobj
.get_ifaceobjs
)
99 ifupdownobj
.logger
.debug(msg
)
101 ifaceobj_getfunc
=ifupdownobj
.get_ifaceobjs
)
103 if not ifupdownobj
.ignore_error(str(e
)):
105 ifupdownobj
.logger
.error(str(e
))
106 # Continue with rest of the modules
109 if err
or ifaceobj
.status
== ifaceStatus
.ERROR
:
110 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
112 if 'up' in op
or 'down' in op
or 'query-checkcurr' in op
:
113 cls
.set_sched_status(False)
115 # Mark success only if the interface was not already
117 status
= (ifaceobj
.status
118 if ifaceobj
.status
== ifaceStatus
.ERROR
119 else ifaceStatus
.SUCCESS
)
120 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
123 if ifupdownobj
.config
.get('addon_scripts_support', '0') == '1':
124 # execute /etc/network/ scripts
125 os
.environ
['IFACE'] = ifaceobj
.name
if ifaceobj
.name
else ''
126 os
.environ
['LOGICAL'] = ifaceobj
.name
if ifaceobj
.name
else ''
127 os
.environ
['METHOD'] = ifaceobj
.addr_method
if ifaceobj
.addr_method
else ''
128 os
.environ
['ADDRFAM'] = ','.join(ifaceobj
.addr_family
) if ifaceobj
.addr_family
else ''
129 for mname
in ifupdownobj
.script_ops
.get(op
, []):
130 ifupdownobj
.logger
.debug('%s: %s : running script %s'
131 %(ifacename
, op
, mname
))
133 utils
.exec_command(mname
, env
=cenv
)
135 ifupdownobj
.log_error('%s: %s %s' % (ifacename
, op
, str(e
)))
138 def run_iface_list_ops(cls
, ifupdownobj
, ifaceobjs
, ops
):
139 """ Runs all operations on a list of interface
140 configurations for the same interface
143 # minor optimization. If operation is 'down', proceed only
144 # if interface exists in the system
145 ifacename
= ifaceobjs
[0].name
146 ifupdownobj
.logger
.info('%s: running ops ...' %ifacename
)
147 if ('down' in ops
[0] and
148 ifaceobjs
[0].type != ifaceType
.BRIDGE_VLAN
and
149 not ifupdownobj
.link_exists(ifacename
)):
150 ifupdownobj
.logger
.debug('%s: does not exist' %ifacename
)
151 # run posthook before you get out of here, so that
152 # appropriate cleanup is done
153 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
155 for ifaceobj
in ifaceobjs
:
156 ifaceobj
.status
= ifaceStatus
.SUCCESS
157 posthookfunc(ifupdownobj
, ifaceobj
, 'down')
160 # first run ifupdownobj handlers. This is good enough
161 # for the first object in the list
162 handler
= ifupdownobj
.ops_handlers
.get(op
)
165 handler(ifupdownobj
, ifaceobjs
[0])
167 if not ifupdownobj
.link_master_slave_ignore_error(str(e
)):
168 ifupdownobj
.logger
.warn('%s: %s'
169 %(ifaceobjs
[0].name
, str(e
)))
171 for ifaceobj
in ifaceobjs
:
172 cls
.run_iface_op(ifupdownobj
, ifaceobj
, op
,
173 cenv
=ifupdownobj
.generate_running_env(ifaceobj
, op
)
174 if ifupdownobj
.config
.get('addon_scripts_support',
175 '0') == '1' else None)
176 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
179 [posthookfunc(ifupdownobj
, ifaceobj
, ops
[0])
180 for ifaceobj
in ifaceobjs
]
182 ifupdownobj
.logger
.warn('%s' %str
(e
))
186 def _check_upperifaces(cls
, ifupdownobj
, ifaceobj
, ops
, parent
,
187 followdependents
=False):
188 """ Check if upperifaces are hanging off us and help caller decide
189 if he can proceed with the ops on this device
191 Returns True or False indicating the caller to proceed with the
194 # proceed only for down operation
195 if 'down' not in ops
[0]:
198 if (ifupdownobj
.flags
.SCHED_SKIP_CHECK_UPPERIFACES
):
201 if (ifupdownflags
.flags
.FORCE
or
202 not ifupdownobj
.flags
.ADDONS_ENABLE
or
203 (not ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
) and
204 ifupdownobj
.config
.get('warn_on_ifdown', '0') == '0' and
205 not ifupdownflags
.flags
.ALL
)):
208 ulist
= ifaceobj
.upperifaces
212 # Get the list of upper ifaces other than the parent
213 tmpulist
= ([u
for u
in ulist
if u
!= parent
] if parent
217 # XXX: This is expensive. Find a cheaper way to do this.
218 # if any of the upperdevs are present,
219 # return false to the caller to skip this interface
221 if ifupdownobj
.link_exists(u
):
222 if not ifupdownflags
.flags
.ALL
:
223 if ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
):
224 ifupdownobj
.logger
.info('%s: skipping interface down,'
225 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
227 ifupdownobj
.logger
.warn('%s: skipping interface down,'
228 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
233 def run_iface_graph(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
234 order
=ifaceSchedulerFlags
.POSTORDER
,
235 followdependents
=True):
236 """ runs interface by traversing all nodes rooted at itself """
238 # Each ifacename can have a list of iface objects
239 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
241 raise Exception('%s: not found' %ifacename
)
243 # Check state of the dependent. If it is already brought up, return
244 if (cls
._STATE
_CHECK
and
245 (ifaceobjs
[0].state
== ifaceState
.from_str(ops
[-1]))):
246 ifupdownobj
.logger
.debug('%s: already processed' %ifacename
)
249 for ifaceobj
in ifaceobjs
:
250 if not cls
._check
_upperifaces
(ifupdownobj
, ifaceobj
,
251 ops
, parent
, followdependents
):
254 # If inorder, run the iface first and then its dependents
255 if order
== ifaceSchedulerFlags
.INORDER
:
256 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
258 for ifaceobj
in ifaceobjs
:
259 # Run lowerifaces or dependents
260 dlist
= ifaceobj
.lowerifaces
262 ifupdownobj
.logger
.debug('%s: found dependents %s'
263 %(ifacename
, str(dlist
)))
265 if not followdependents
:
266 # XXX: this is yet another extra step,
267 # but is needed for interfaces that are
268 # implicit dependents. even though we are asked to
269 # not follow dependents, we must follow the ones
270 # that dont have user given config. Because we own them
271 new_dlist
= [d
for d
in dlist
272 if ifupdownobj
.is_iface_noconfig(d
)]
274 cls
.run_iface_list(ifupdownobj
, new_dlist
, ops
,
275 ifacename
, order
, followdependents
,
276 continueonfailure
=False)
278 cls
.run_iface_list(ifupdownobj
, dlist
, ops
,
281 continueonfailure
=False)
283 if (ifupdownobj
.ignore_error(str(e
))):
286 # Dont bring the iface up if children did not come up
287 ifaceobj
.set_state_n_status(ifaceState
.NEW
,
290 if order
== ifaceSchedulerFlags
.POSTORDER
:
291 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
294 def run_iface_list(cls
, ifupdownobj
, ifacenames
,
295 ops
, parent
=None, order
=ifaceSchedulerFlags
.POSTORDER
,
296 followdependents
=True, continueonfailure
=True):
297 """ Runs interface list """
299 for ifacename
in ifacenames
:
301 cls
.run_iface_graph(ifupdownobj
, ifacename
, ops
, parent
,
302 order
, followdependents
)
304 if continueonfailure
:
305 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
306 traceback
.print_tb(sys
.exc_info()[2])
307 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
310 if (ifupdownobj
.ignore_error(str(e
))):
313 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
316 def run_iface_graph_upper(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
317 followdependents
=True, skip_root
=False):
318 """ runs interface by traversing all nodes rooted at itself """
320 # Each ifacename can have a list of iface objects
321 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
323 raise Exception('%s: not found' %ifacename
)
325 if (cls
._STATE
_CHECK
and
326 (ifaceobjs
[0].state
== ifaceState
.from_str(ops
[-1]))):
327 ifupdownobj
.logger
.debug('%s: already processed' %ifacename
)
331 # run the iface first and then its upperifaces
332 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
333 for ifaceobj
in ifaceobjs
:
335 ulist
= ifaceobj
.upperifaces
337 ifupdownobj
.logger
.debug('%s: found upperifaces %s'
338 %(ifacename
, str(ulist
)))
340 cls
.run_iface_list_upper(ifupdownobj
, ulist
, ops
,
343 continueonfailure
=True)
345 if (ifupdownobj
.ignore_error(str(e
))):
351 def run_iface_list_upper(cls
, ifupdownobj
, ifacenames
,
352 ops
, parent
=None, followdependents
=True,
353 continueonfailure
=True, skip_root
=False):
354 """ Runs interface list """
356 for ifacename
in ifacenames
:
358 cls
.run_iface_graph_upper(ifupdownobj
, ifacename
, ops
, parent
,
359 followdependents
, skip_root
)
361 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
362 traceback
.print_tb(sys
.exc_info()[2])
363 ifupdownobj
.logger
.warn('%s : %s' %(ifacename
, str(e
)))
367 def _get_valid_upperifaces(cls
, ifupdownobj
, ifacenames
,
369 """ Recursively find valid upperifaces
371 valid upperifaces are:
372 - An upperiface which had no user config (example builtin
373 interfaces. usually vlan interfaces.)
374 - or had config and previously up
375 - and interface currently does not exist
376 - or is a bridge (because if your upperiface was a bridge
377 - u will have to execute up on the bridge
378 to enslave the port and apply bridge attributes to the port) """
381 for ifacename
in ifacenames
:
383 ifaceobj
= ifupdownobj
.get_ifaceobj_first(ifacename
)
386 ulist
= Set(ifaceobj
.upperifaces
).difference(upperifacenames
)
389 uifaceobj
= ifupdownobj
.get_ifaceobj_first(u
)
392 has_config
= not (uifaceobj
.priv_flags
and
393 uifaceobj
.priv_flags
.NOCONFIG
)
394 if (((has_config
and ifupdownobj
.get_ifaceobjs_saved(u
)) or
395 not has_config
) and (not ifupdownobj
.link_exists(u
)
396 # Do this always for a bridge. Note that this is
397 # not done for a vlan aware bridge because,
398 # in the vlan aware bridge case, the bridge module
399 # applies the bridge port configuration on the port
400 # when up is scheduled on the port.
401 or (uifaceobj
.link_kind
== ifaceLinkKind
.BRIDGE
))):
403 upperifacenames
.extend(nulist
)
404 allupperifacenames
.extend(upperifacenames
)
406 cls
._get
_valid
_upperifaces
(ifupdownobj
, upperifacenames
,
411 def run_upperifaces(cls
, ifupdownobj
, ifacenames
, ops
,
412 continueonfailure
=True):
413 """ Run through valid upperifaces """
416 cls
._get
_valid
_upperifaces
(ifupdownobj
, ifacenames
, upperifaces
)
419 # dump valid upperifaces
420 ifupdownobj
.logger
.debug(upperifaces
)
421 for u
in upperifaces
:
423 ifaceobjs
= ifupdownobj
.get_ifaceobjs(u
)
426 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
428 if continueonfailure
:
429 ifupdownobj
.logger
.warn('%s' %str
(e
))
432 def _dump_dependency_info(cls
, ifupdownobj
, ifacenames
,
433 dependency_graph
=None, indegrees
=None):
434 ifupdownobj
.logger
.info('{\n')
435 ifupdownobj
.logger
.info('\nifaceobjs:')
436 for iname
in ifacenames
:
437 iobjs
= ifupdownobj
.get_ifaceobjs(iname
)
439 iobj
.dump(ifupdownobj
.logger
)
440 if (dependency_graph
):
441 ifupdownobj
.logger
.info('\nDependency Graph:')
442 ifupdownobj
.logger
.info(dependency_graph
)
444 ifupdownobj
.logger
.info('\nIndegrees:')
445 ifupdownobj
.logger
.info(indegrees
)
446 ifupdownobj
.logger
.info('}\n')
449 def get_sorted_iface_list(cls
, ifupdownobj
, ifacenames
, ops
,
450 dependency_graph
, indegrees
=None):
451 if len(ifacenames
) == 1:
453 # Get a sorted list of all interfaces
455 indegrees
= OrderedDict()
456 for ifacename
in dependency_graph
.keys():
457 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
459 #cls._dump_dependency_info(ifupdownobj, ifacenames,
460 # dependency_graph, indegrees)
462 ifacenames_all_sorted
= graph
.topological_sort_graphs_all(
463 dependency_graph
, indegrees
)
464 # if ALL was set, return all interfaces
465 if ifupdownflags
.flags
.ALL
:
466 return ifacenames_all_sorted
468 # else return ifacenames passed as argument in sorted order
469 ifacenames_sorted
= []
470 [ifacenames_sorted
.append(ifacename
)
471 for ifacename
in ifacenames_all_sorted
472 if ifacename
in ifacenames
]
473 return ifacenames_sorted
476 def sched_ifaces(cls
, ifupdownobj
, ifacenames
, ops
,
477 dependency_graph
=None, indegrees
=None,
478 order
=ifaceSchedulerFlags
.POSTORDER
,
479 followdependents
=True, skipupperifaces
=False, sort
=False):
480 """ runs interface configuration modules on interfaces passed as
481 argument. Runs topological sort on interface dependency graph.
484 **ifupdownobj** (object): ifupdownMain object
486 **ifacenames** (list): list of interface names
488 **ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
490 **dependency_graph** (dict): dependency graph in adjacency list format
493 **indegrees** (dict): indegree array of the dependency graph
495 **order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
497 **followdependents** (bool): follow dependent interfaces if true
499 **sort** (bool): sort ifacelist in the case where ALL is not set
504 # if ALL/auto interfaces are specified,
505 # - walk the dependency tree in postorder or inorder depending
507 # (This is to run interfaces correctly in order)
509 # - sort iface list if the ifaces belong to a "class"
510 # - else just run iface list in the order they were specified
512 # Run any upperifaces if available
514 followupperifaces
= False
516 skip_ifacesort
= int(ifupdownobj
.config
.get('skip_ifacesort', '0'))
517 if not skip_ifacesort
and not indegrees
:
518 indegrees
= OrderedDict()
519 for ifacename
in dependency_graph
.keys():
520 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
522 if not ifupdownflags
.flags
.ALL
:
524 # If there is any interface that does not exist, maybe it
525 # is a logical interface and we have to followupperifaces
526 # when it comes up, so lets get that list.
527 if any([True for i
in ifacenames
528 if ifupdownobj
.must_follow_upperifaces(i
)]):
529 followupperifaces
= (True if
530 [i
for i
in ifacenames
531 if not ifupdownobj
.link_exists(i
)]
533 # sort interfaces only if the caller asked to sort
534 # and skip_ifacesort is not on.
535 if not skip_ifacesort
and sort
:
536 run_queue
= cls
.get_sorted_iface_list(ifupdownobj
, ifacenames
,
537 ops
, dependency_graph
, indegrees
)
538 if run_queue
and 'up' in ops
[0]:
541 # if -a is set, we pick the interfaces
542 # that have no parents and use a sorted list of those
543 if not skip_ifacesort
:
544 sorted_ifacenames
= cls
.get_sorted_iface_list(ifupdownobj
,
545 ifacenames
, ops
, dependency_graph
,
547 if sorted_ifacenames
:
548 # pick interfaces that user asked
549 # and those that dont have any dependents first
550 [run_queue
.append(ifacename
)
551 for ifacename
in sorted_ifacenames
552 if ifacename
in ifacenames
and
553 not indegrees
.get(ifacename
)]
554 ifupdownobj
.logger
.debug('graph roots (interfaces that ' +
555 'dont have dependents):' + ' %s' %str
(run_queue
))
557 ifupdownobj
.logger
.warn('interface sort returned None')
559 # If queue not present, just run interfaces that were asked by the
562 run_queue
= list(ifacenames
)
563 # if we are taking the order of interfaces as specified
564 # in the interfaces file, we should reverse the list if we
565 # want to down. This can happen if 'skip_ifacesort'
571 cls
.run_iface_list(ifupdownobj
, run_queue
, ops
,
572 parent
=None, order
=order
,
573 followdependents
=followdependents
)
574 if not cls
.get_sched_status():
577 if (not skipupperifaces
and
578 ifupdownobj
.config
.get('skip_upperifaces', '0') == '0' and
579 ((not ifupdownflags
.flags
.ALL
and followdependents
) or
580 followupperifaces
) and
582 # If user had given a set of interfaces to bring up
583 # try and execute 'up' on the upperifaces
584 ifupdownobj
.logger
.info('running upperifaces (parent interfaces) ' +
587 # upperiface bring up is best effort.
588 # eg case: if we are bringing up a bridge port
589 # this section does an 'ifup on the bridge'
590 # so that the recently up'ed bridge port gets enslaved
591 # to the bridge. But the up on the bridge may
592 # throw out more errors if the bridge is not
593 # in the correct state. Lets not surprise
594 # the user with such errors when he has
595 # only requested to bring up the bridge port.
596 cls
._STATE
_CHECK
= False
597 ifupdownflags
.flags
.IGNORE_ERRORS
= True
598 cls
.run_upperifaces(ifupdownobj
, ifacenames
, ops
)
600 ifupdownflags
.flags
.IGNORE_ERRORS
= False
601 cls
._STATE
_CHECK
= True
602 # upperiface bringup is best effort, so dont propagate errors
603 # reset scheduler status to True
604 cls
.set_sched_status(True)