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
.iface
import *
18 from ifupdown2
.ifupdown
.utils
import utils
19 from ifupdown2
.ifupdown
.statemanager
import *
21 import ifupdown2
.ifupdown
.policymanager
as policymanager
22 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
24 from ifupdown
.graph
import *
25 from ifupdown
.iface
import *
26 from ifupdown
.utils
import utils
27 from ifupdown
.statemanager
import *
29 import ifupdown
.ifupdownflags
as ifupdownflags
30 import ifupdown
.policymanager
as policymanager
33 class ifaceSchedulerFlags():
34 """ Enumerates scheduler flags """
39 class ifaceScheduler():
40 """ scheduler functions to schedule configuration of interfaces.
42 supports scheduling of interfaces serially in plain interface list
43 or dependency graph format.
51 VRF_MGMT_DEVNAME
= policymanager
.policymanager_api
.get_module_globals(
53 attr
="vrf-mgmt-devname"
58 cls
._STATE
_CHECK
= True
59 cls
._SCHED
_STATUS
= True
62 def get_sched_status(cls
):
63 return cls
._SCHED
_STATUS
66 def set_sched_status(cls
, state
):
67 cls
._SCHED
_STATUS
= state
70 def run_iface_op(cls
, ifupdownobj
, ifaceobj
, op
, cenv
=None):
71 """ Runs sub operation on an interface """
72 ifacename
= ifaceobj
.name
74 if ifupdownobj
.type and ifupdownobj
.type != ifaceobj
.type:
77 if not ifupdownobj
.flags
.ADDONS_ENABLE
: return
78 if op
== 'query-checkcurr':
79 query_ifaceobj
=ifupdownobj
.create_n_save_ifaceobjcurr(ifaceobj
)
80 # If not type bridge vlan and the object does not exist,
81 # mark not found and return
82 if (not ifupdownobj
.link_exists(ifaceobj
.name
) and
83 ifaceobj
.type != ifaceType
.BRIDGE_VLAN
):
84 query_ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
87 for mname
in ifupdownobj
.module_ops
.get(op
):
88 m
= ifupdownobj
.modules
.get(mname
)
92 msg
= ('%s: %s : running module %s' %(ifacename
, op
, mname
))
93 if op
== 'query-checkcurr':
94 # Dont check curr if the interface object was
96 if (ifaceobj
.priv_flags
and
97 ifaceobj
.priv_flags
.NOCONFIG
):
99 ifupdownobj
.logger
.debug(msg
)
100 m
.run(ifaceobj
, op
, query_ifaceobj
,
101 ifaceobj_getfunc
=ifupdownobj
.get_ifaceobjs
)
103 ifupdownobj
.logger
.debug(msg
)
105 ifaceobj_getfunc
=ifupdownobj
.get_ifaceobjs
)
107 if not ifupdownobj
.ignore_error(str(e
)):
110 #traceback.print_exc()
111 ifupdownobj
.logger
.error(str(e
))
112 # Continue with rest of the modules
115 if err
or ifaceobj
.status
== ifaceStatus
.ERROR
:
116 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
118 if 'up' in op
or 'down' in op
or 'query-checkcurr' in op
:
119 cls
.set_sched_status(False)
121 # Mark success only if the interface was not already
123 status
= (ifaceobj
.status
124 if ifaceobj
.status
== ifaceStatus
.ERROR
125 else ifaceStatus
.SUCCESS
)
126 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
129 if ifupdownobj
.config
.get('addon_scripts_support', '0') == '1':
130 # execute /etc/network/ scripts
131 os
.environ
['IFACE'] = ifaceobj
.name
if ifaceobj
.name
else ''
132 os
.environ
['LOGICAL'] = ifaceobj
.name
if ifaceobj
.name
else ''
133 os
.environ
['METHOD'] = ifaceobj
.addr_method
if ifaceobj
.addr_method
else ''
134 os
.environ
['ADDRFAM'] = ','.join(ifaceobj
.addr_family
) if ifaceobj
.addr_family
else ''
135 for mname
in ifupdownobj
.script_ops
.get(op
, []):
136 ifupdownobj
.logger
.debug('%s: %s : running script %s'
137 %(ifacename
, op
, mname
))
139 utils
.exec_command(mname
, env
=cenv
)
141 if "permission denied" in str(e
).lower():
142 ifupdownobj
.logger
.warning('%s: %s %s' % (ifacename
, op
, str(e
)))
144 ifupdownobj
.log_error('%s: %s %s' % (ifacename
, op
, str(e
)))
147 def run_iface_list_ops(cls
, ifupdownobj
, ifaceobjs
, ops
):
148 """ Runs all operations on a list of interface
149 configurations for the same interface
152 # minor optimization. If operation is 'down', proceed only
153 # if interface exists in the system
154 ifacename
= ifaceobjs
[0].name
155 ifupdownobj
.logger
.info('%s: running ops ...' %ifacename
)
156 if ('down' in ops
[0] and
157 ifaceobjs
[0].type != ifaceType
.BRIDGE_VLAN
and
158 ifaceobjs
[0].addr_method
!= 'ppp' and
159 not ifupdownobj
.link_exists(ifacename
)):
160 ifupdownobj
.logger
.debug('%s: does not exist' %ifacename
)
161 # run posthook before you get out of here, so that
162 # appropriate cleanup is done
163 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
165 for ifaceobj
in ifaceobjs
:
166 ifaceobj
.status
= ifaceStatus
.SUCCESS
167 posthookfunc(ifupdownobj
, ifaceobj
, 'down')
170 # first run ifupdownobj handlers. This is good enough
171 # for the first object in the list
172 handler
= ifupdownobj
.ops_handlers
.get(op
)
175 handler(ifupdownobj
, ifaceobjs
[0])
177 if not ifupdownobj
.link_master_slave_ignore_error(str(e
)):
178 ifupdownobj
.logger
.warn('%s: %s'
179 %(ifaceobjs
[0].name
, str(e
)))
181 for ifaceobj
in ifaceobjs
:
182 cls
.run_iface_op(ifupdownobj
, ifaceobj
, op
,
183 cenv
=ifupdownobj
.generate_running_env(ifaceobj
, op
)
184 if ifupdownobj
.config
.get('addon_scripts_support',
185 '0') == '1' else None)
186 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
189 [posthookfunc(ifupdownobj
, ifaceobj
, ops
[0])
190 for ifaceobj
in ifaceobjs
]
192 ifupdownobj
.logger
.warn('%s' %str
(e
))
196 def _check_upperifaces(cls
, ifupdownobj
, ifaceobj
, ops
, parent
,
197 followdependents
=False):
198 """ Check if upperifaces are hanging off us and help caller decide
199 if he can proceed with the ops on this device
201 Returns True or False indicating the caller to proceed with the
204 # proceed only for down operation
205 if 'down' not in ops
[0]:
208 if (ifupdownobj
.flags
.SCHED_SKIP_CHECK_UPPERIFACES
):
211 if (ifupdownflags
.flags
.FORCE
or
212 not ifupdownobj
.flags
.ADDONS_ENABLE
or
213 (not ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
) and
214 ifupdownobj
.config
.get('warn_on_ifdown', '0') == '0' and
215 not ifupdownflags
.flags
.ALL
)):
218 ulist
= ifaceobj
.upperifaces
222 # Get the list of upper ifaces other than the parent
223 tmpulist
= ([u
for u
in ulist
if u
!= parent
] if parent
227 # XXX: This is expensive. Find a cheaper way to do this.
228 # if any of the upperdevs are present,
229 # return false to the caller to skip this interface
231 if ifupdownobj
.link_exists(u
):
232 if not ifupdownflags
.flags
.ALL
:
233 if ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
):
234 ifupdownobj
.logger
.info('%s: skipping interface down,'
235 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
237 ifupdownobj
.logger
.warn('%s: skipping interface down,'
238 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
243 def run_iface_graph(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
244 order
=ifaceSchedulerFlags
.POSTORDER
,
245 followdependents
=True):
246 """ runs interface by traversing all nodes rooted at itself """
248 # Each ifacename can have a list of iface objects
249 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
251 raise Exception('%s: not found' %ifacename
)
253 # Check state of the dependent. If it is already brought up, return
254 if (cls
._STATE
_CHECK
and
255 (ifaceobjs
[0].state
== ifaceState
.from_str(ops
[-1]))):
256 ifupdownobj
.logger
.debug('%s: already processed' %ifacename
)
259 for ifaceobj
in ifaceobjs
:
260 if not cls
._check
_upperifaces
(ifupdownobj
, ifaceobj
,
261 ops
, parent
, followdependents
):
264 # If inorder, run the iface first and then its dependents
265 if order
== ifaceSchedulerFlags
.INORDER
:
266 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
268 for ifaceobj
in ifaceobjs
:
269 # Run lowerifaces or dependents
270 dlist
= ifaceobj
.lowerifaces
273 if ifaceobj
.link_kind
== ifaceLinkKind
.VRF
:
274 # remove non-auto lowerifaces from 'dlist'
275 for lower_ifname
in list(dlist
):
276 for lower_ifaceobj
in ifupdownobj
.get_ifaceobjs(lower_ifname
) or []:
277 if lower_ifaceobj
and not lower_ifaceobj
.auto
and ifaceobj
.name
== cls
.VRF_MGMT_DEVNAME
:
278 dlist
.remove(lower_ifname
)
280 ifupdownobj
.logger
.debug('%s: found dependents %s'
281 %(ifacename
, str(dlist
)))
283 if not followdependents
:
284 # XXX: this is yet another extra step,
285 # but is needed for interfaces that are
286 # implicit dependents. even though we are asked to
287 # not follow dependents, we must follow the ones
288 # that dont have user given config. Because we own them
289 new_dlist
= [d
for d
in dlist
290 if ifupdownobj
.is_iface_noconfig(d
)]
292 cls
.run_iface_list(ifupdownobj
, new_dlist
, ops
,
293 ifacename
, order
, followdependents
,
294 continueonfailure
=False)
296 cls
.run_iface_list(ifupdownobj
, dlist
, ops
,
299 continueonfailure
=False)
301 if (ifupdownobj
.ignore_error(str(e
))):
304 # Dont bring the iface up if children did not come up
305 ifaceobj
.set_state_n_status(ifaceState
.NEW
,
308 if order
== ifaceSchedulerFlags
.POSTORDER
:
309 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
312 def run_iface_list(cls
, ifupdownobj
, ifacenames
,
313 ops
, parent
=None, order
=ifaceSchedulerFlags
.POSTORDER
,
314 followdependents
=True, continueonfailure
=True):
315 """ Runs interface list """
317 for ifacename
in ifacenames
:
319 cls
.run_iface_graph(ifupdownobj
, ifacename
, ops
, parent
,
320 order
, followdependents
)
322 if continueonfailure
:
323 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
324 traceback
.print_tb(sys
.exc_info()[2])
325 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
328 if (ifupdownobj
.ignore_error(str(e
))):
331 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
334 def run_iface_graph_upper(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
335 followdependents
=True, skip_root
=False):
336 """ runs interface by traversing all nodes rooted at itself """
338 # Each ifacename can have a list of iface objects
339 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
341 raise Exception('%s: not found' %ifacename
)
343 if (cls
._STATE
_CHECK
and
344 (ifaceobjs
[0].state
== ifaceState
.from_str(ops
[-1]))):
345 ifupdownobj
.logger
.debug('%s: already processed' %ifacename
)
349 # run the iface first and then its upperifaces
350 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
351 for ifaceobj
in ifaceobjs
:
353 ulist
= ifaceobj
.upperifaces
355 ifupdownobj
.logger
.debug('%s: found upperifaces %s'
356 %(ifacename
, str(ulist
)))
358 cls
.run_iface_list_upper(ifupdownobj
, ulist
, ops
,
361 continueonfailure
=True)
363 if (ifupdownobj
.ignore_error(str(e
))):
369 def run_iface_list_upper(cls
, ifupdownobj
, ifacenames
,
370 ops
, parent
=None, followdependents
=True,
371 continueonfailure
=True, skip_root
=False):
372 """ Runs interface list """
374 for ifacename
in ifacenames
:
376 cls
.run_iface_graph_upper(ifupdownobj
, ifacename
, ops
, parent
,
377 followdependents
, skip_root
)
379 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
380 traceback
.print_tb(sys
.exc_info()[2])
381 ifupdownobj
.logger
.warn('%s : %s' %(ifacename
, str(e
)))
385 def _get_valid_upperifaces(cls
, ifupdownobj
, ifacenames
,
387 """ Recursively find valid upperifaces
389 valid upperifaces are:
390 - An upperiface which had no user config (example builtin
391 interfaces. usually vlan interfaces.)
392 - or had config and previously up
393 - and interface currently does not exist
394 - or is a bridge (because if your upperiface was a bridge
395 - u will have to execute up on the bridge
396 to enslave the port and apply bridge attributes to the port) """
399 for ifacename
in ifacenames
:
401 ifaceobj
= ifupdownobj
.get_ifaceobj_first(ifacename
)
404 ulist
= Set(ifaceobj
.upperifaces
).difference(upperifacenames
)
407 uifaceobj
= ifupdownobj
.get_ifaceobj_first(u
)
410 has_config
= not (uifaceobj
.priv_flags
and
411 uifaceobj
.priv_flags
.NOCONFIG
)
412 if (((has_config
and ifupdownobj
.get_ifaceobjs_saved(u
)) or
413 not has_config
) and (not ifupdownobj
.link_exists(u
)
414 # Do this always for a bridge. Note that this is
415 # not done for a vlan aware bridge because,
416 # in the vlan aware bridge case, the bridge module
417 # applies the bridge port configuration on the port
418 # when up is scheduled on the port.
419 or (uifaceobj
.link_kind
== ifaceLinkKind
.BRIDGE
))):
421 upperifacenames
.extend(nulist
)
422 allupperifacenames
.extend(upperifacenames
)
424 cls
._get
_valid
_upperifaces
(ifupdownobj
, upperifacenames
,
429 def run_upperifaces(cls
, ifupdownobj
, ifacenames
, ops
,
430 continueonfailure
=True):
431 """ Run through valid upperifaces """
434 cls
._get
_valid
_upperifaces
(ifupdownobj
, ifacenames
, upperifaces
)
437 # dump valid upperifaces
438 ifupdownobj
.logger
.debug(upperifaces
)
439 for u
in upperifaces
:
441 ifaceobjs
= ifupdownobj
.get_ifaceobjs(u
)
444 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
446 if continueonfailure
:
447 ifupdownobj
.logger
.warn('%s' %str
(e
))
450 def _dump_dependency_info(cls
, ifupdownobj
, ifacenames
,
451 dependency_graph
=None, indegrees
=None):
452 ifupdownobj
.logger
.info('{\n')
453 ifupdownobj
.logger
.info('\nifaceobjs:')
454 for iname
in ifacenames
:
455 iobjs
= ifupdownobj
.get_ifaceobjs(iname
)
457 iobj
.dump(ifupdownobj
.logger
)
458 if (dependency_graph
):
459 ifupdownobj
.logger
.info('\nDependency Graph:')
460 ifupdownobj
.logger
.info(dependency_graph
)
462 ifupdownobj
.logger
.info('\nIndegrees:')
463 ifupdownobj
.logger
.info(indegrees
)
464 ifupdownobj
.logger
.info('}\n')
467 def get_sorted_iface_list(cls
, ifupdownobj
, ifacenames
, ops
,
468 dependency_graph
, indegrees
=None):
469 if len(ifacenames
) == 1:
471 # Get a sorted list of all interfaces
473 indegrees
= OrderedDict()
474 for ifacename
in dependency_graph
.keys():
475 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
477 #cls._dump_dependency_info(ifupdownobj, ifacenames,
478 # dependency_graph, indegrees)
480 ifacenames_all_sorted
= graph
.topological_sort_graphs_all(
481 dependency_graph
, indegrees
)
482 # if ALL was set, return all interfaces
483 if ifupdownflags
.flags
.ALL
:
484 return ifacenames_all_sorted
486 # else return ifacenames passed as argument in sorted order
487 ifacenames_sorted
= []
488 [ifacenames_sorted
.append(ifacename
)
489 for ifacename
in ifacenames_all_sorted
490 if ifacename
in ifacenames
]
491 return ifacenames_sorted
494 def sched_ifaces(cls
, ifupdownobj
, ifacenames
, ops
,
495 dependency_graph
=None, indegrees
=None,
496 order
=ifaceSchedulerFlags
.POSTORDER
,
497 followdependents
=True, skipupperifaces
=False, sort
=False):
498 """ runs interface configuration modules on interfaces passed as
499 argument. Runs topological sort on interface dependency graph.
502 **ifupdownobj** (object): ifupdownMain object
504 **ifacenames** (list): list of interface names
506 **ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
508 **dependency_graph** (dict): dependency graph in adjacency list format
511 **indegrees** (dict): indegree array of the dependency graph
513 **order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
515 **followdependents** (bool): follow dependent interfaces if true
517 **sort** (bool): sort ifacelist in the case where ALL is not set
522 # if ALL/auto interfaces are specified,
523 # - walk the dependency tree in postorder or inorder depending
525 # (This is to run interfaces correctly in order)
527 # - sort iface list if the ifaces belong to a "class"
528 # - else just run iface list in the order they were specified
530 # Run any upperifaces if available
532 followupperifaces
= False
534 skip_ifacesort
= int(ifupdownobj
.config
.get('skip_ifacesort', '0'))
535 if not skip_ifacesort
and not indegrees
:
536 indegrees
= OrderedDict()
537 for ifacename
in dependency_graph
.keys():
538 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
540 if not ifupdownflags
.flags
.ALL
:
542 # If there is any interface that does not exist, maybe it
543 # is a logical interface and we have to followupperifaces
544 # when it comes up, so lets get that list.
545 if any([True for i
in ifacenames
546 if ifupdownobj
.must_follow_upperifaces(i
)]):
547 followupperifaces
= (True if
548 [i
for i
in ifacenames
549 if not ifupdownobj
.link_exists(i
)]
551 # sort interfaces only if the caller asked to sort
552 # and skip_ifacesort is not on.
553 if not skip_ifacesort
and sort
:
554 run_queue
= cls
.get_sorted_iface_list(ifupdownobj
, ifacenames
,
555 ops
, dependency_graph
, indegrees
)
556 if run_queue
and 'up' in ops
[0]:
559 # if -a is set, we pick the interfaces
560 # that have no parents and use a sorted list of those
561 if not skip_ifacesort
:
562 sorted_ifacenames
= cls
.get_sorted_iface_list(ifupdownobj
,
563 ifacenames
, ops
, dependency_graph
,
565 if sorted_ifacenames
:
566 # pick interfaces that user asked
567 # and those that dont have any dependents first
568 [run_queue
.append(ifacename
)
569 for ifacename
in sorted_ifacenames
570 if ifacename
in ifacenames
and
571 not indegrees
.get(ifacename
)]
572 ifupdownobj
.logger
.debug('graph roots (interfaces that ' +
573 'dont have dependents):' + ' %s' %str
(run_queue
))
575 ifupdownobj
.logger
.warn('interface sort returned None')
577 # If queue not present, just run interfaces that were asked by the
580 run_queue
= list(ifacenames
)
581 # if we are taking the order of interfaces as specified
582 # in the interfaces file, we should reverse the list if we
583 # want to down. This can happen if 'skip_ifacesort'
589 cls
.run_iface_list(ifupdownobj
, run_queue
, ops
,
590 parent
=None, order
=order
,
591 followdependents
=followdependents
)
592 if not cls
.get_sched_status():
595 if (not skipupperifaces
and
596 ifupdownobj
.config
.get('skip_upperifaces', '0') == '0' and
597 ((not ifupdownflags
.flags
.ALL
and followdependents
) or
598 followupperifaces
) and
600 # If user had given a set of interfaces to bring up
601 # try and execute 'up' on the upperifaces
602 ifupdownobj
.logger
.info('running upperifaces (parent interfaces) ' +
605 # upperiface bring up is best effort.
606 # eg case: if we are bringing up a bridge port
607 # this section does an 'ifup on the bridge'
608 # so that the recently up'ed bridge port gets enslaved
609 # to the bridge. But the up on the bridge may
610 # throw out more errors if the bridge is not
611 # in the correct state. Lets not surprise
612 # the user with such errors when he has
613 # only requested to bring up the bridge port.
614 cls
._STATE
_CHECK
= False
615 ifupdownflags
.flags
.IGNORE_ERRORS
= True
616 cls
.run_upperifaces(ifupdownobj
, ifacenames
, ops
)
618 ifupdownflags
.flags
.IGNORE_ERRORS
= False
619 cls
._STATE
_CHECK
= True
620 # upperiface bringup is best effort, so dont propagate errors
621 # reset scheduler status to True
622 cls
.set_sched_status(True)