3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
14 from ifupdown2
.ifupdown
.graph
import *
15 from ifupdown2
.ifupdown
.iface
import *
16 from ifupdown2
.ifupdown
.utils
import utils
17 from ifupdown2
.ifupdown
.statemanager
import *
19 import ifupdown2
.ifupdown
.policymanager
as policymanager
20 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
21 except (ImportError, ModuleNotFoundError
):
22 from ifupdown
.graph
import *
23 from ifupdown
.iface
import *
24 from ifupdown
.utils
import utils
25 from ifupdown
.statemanager
import *
27 import ifupdown
.ifupdownflags
as ifupdownflags
28 import ifupdown
.policymanager
as policymanager
31 class ifaceSchedulerFlags():
32 """ Enumerates scheduler flags """
37 class ifaceScheduler():
38 """ scheduler functions to schedule configuration of interfaces.
40 supports scheduling of interfaces serially in plain interface list
41 or dependency graph format.
49 VRF_MGMT_DEVNAME
= policymanager
.policymanager_api
.get_module_globals(
51 attr
="vrf-mgmt-devname"
56 cls
._STATE
_CHECK
= True
57 cls
._SCHED
_STATUS
= True
60 def get_sched_status(cls
):
61 return cls
._SCHED
_STATUS
64 def set_sched_status(cls
, state
):
65 cls
._SCHED
_STATUS
= state
68 def run_iface_op(cls
, ifupdownobj
, ifaceobj
, op
, cenv
=None):
69 """ Runs sub operation on an interface """
70 ifacename
= ifaceobj
.name
72 if ifupdownobj
.type and ifupdownobj
.type != ifaceobj
.type:
75 if not ifupdownobj
.flags
.ADDONS_ENABLE
: return
76 if op
== 'query-checkcurr':
77 query_ifaceobj
=ifupdownobj
.create_n_save_ifaceobjcurr(ifaceobj
)
78 # If not type bridge vlan and the object does not exist,
79 # mark not found and return
80 if (not ifupdownobj
.link_exists(ifaceobj
.name
) and
81 ifaceobj
.type != ifaceType
.BRIDGE_VLAN
):
82 query_ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
85 for mname
in ifupdownobj
.module_ops
.get(op
):
86 m
= ifupdownobj
.modules
.get(mname
)
90 msg
= ('%s: %s : running module %s' %(ifacename
, op
, mname
))
91 if op
== 'query-checkcurr':
92 # Dont check curr if the interface object was
94 if (ifaceobj
.priv_flags
and
95 ifaceobj
.priv_flags
.NOCONFIG
):
97 ifupdownobj
.logger
.debug(msg
)
98 m
.run(ifaceobj
, op
, query_ifaceobj
,
99 ifaceobj_getfunc
=ifupdownobj
.get_ifaceobjs
)
101 ifupdownobj
.logger
.debug(msg
)
103 ifaceobj_getfunc
=ifupdownobj
.get_ifaceobjs
)
104 except Exception as e
:
105 if not ifupdownobj
.ignore_error(str(e
)):
108 #traceback.print_exc()
110 ifupdownobj
.logger
.error(str(e
))
111 # Continue with rest of the modules
114 if err
or ifaceobj
.status
== ifaceStatus
.ERROR
:
115 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
117 if 'up' in op
or 'down' in op
or 'query-checkcurr' in op
:
118 cls
.set_sched_status(False)
120 # Mark success only if the interface was not already
122 status
= (ifaceobj
.status
123 if ifaceobj
.status
== ifaceStatus
.ERROR
124 else ifaceStatus
.SUCCESS
)
125 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
128 if ifupdownobj
.config
.get('addon_scripts_support', '0') == '1':
129 # execute /etc/network/ scripts
130 command_env
= (cenv
or {}).copy()
132 "IFACE": ifaceobj
.name
if ifaceobj
.name
else "",
133 "LOGICAL": ifaceobj
.name
if ifaceobj
.name
else "",
134 "METHOD": ifaceobj
.addr_method
if ifaceobj
.addr_method
else "",
135 "ADDRFAM": ','.join(ifaceobj
.addr_family
) if ifaceobj
.addr_family
else "",
138 for mname
in ifupdownobj
.script_ops
.get(op
, []):
139 ifupdownobj
.logger
.debug("%s: %s : running script %s" % (ifacename
, op
, mname
))
141 utils
.exec_command(mname
, env
=command_env
)
142 except Exception as e
:
143 if "permission denied" in str(e
).lower():
144 ifupdownobj
.logger
.warning('%s: %s %s' % (ifacename
, op
, str(e
)))
146 ifupdownobj
.log_error('%s: %s %s' % (ifacename
, op
, str(e
)))
149 def run_iface_list_ops(cls
, ifupdownobj
, ifaceobjs
, ops
):
150 """ Runs all operations on a list of interface
151 configurations for the same interface
154 # minor optimization. If operation is 'down', proceed only
155 # if interface exists in the system
156 ifacename
= ifaceobjs
[0].name
157 ifupdownobj
.logger
.info('%s: running ops ...' %ifacename
)
158 if ('down' in ops
[0] and
159 ifaceobjs
[0].type != ifaceType
.BRIDGE_VLAN
and
160 ifaceobjs
[0].addr_method
!= 'ppp' and
161 not ifupdownobj
.link_exists(ifacename
)):
162 ifupdownobj
.logger
.debug('%s: does not exist' %ifacename
)
163 # run posthook before you get out of here, so that
164 # appropriate cleanup is done
165 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
167 for ifaceobj
in ifaceobjs
:
168 ifaceobj
.status
= ifaceStatus
.SUCCESS
169 posthookfunc(ifupdownobj
, ifaceobj
, 'down')
172 # first run ifupdownobj handlers. This is good enough
173 # for the first object in the list
174 handler
= ifupdownobj
.ops_handlers
.get(op
)
177 handler(ifupdownobj
, ifaceobjs
[0])
178 except Exception as e
:
179 if not ifupdownobj
.link_master_slave_ignore_error(str(e
)):
180 ifupdownobj
.logger
.warning('%s: %s'
181 %(ifaceobjs
[0].name
, str(e
)))
183 for ifaceobj
in ifaceobjs
:
184 cls
.run_iface_op(ifupdownobj
, ifaceobj
, op
,
185 cenv
=ifupdownobj
.generate_running_env(ifaceobj
, op
)
186 if ifupdownobj
.config
.get('addon_scripts_support',
187 '0') == '1' else None)
188 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
191 [posthookfunc(ifupdownobj
, ifaceobj
, ops
[0])
192 for ifaceobj
in ifaceobjs
]
193 except Exception as e
:
194 ifupdownobj
.logger
.warning('%s' %str
(e
))
198 def _check_upperifaces(cls
, ifupdownobj
, ifaceobj
, ops
, parent
,
199 followdependents
=False):
200 """ Check if upperifaces are hanging off us and help caller decide
201 if he can proceed with the ops on this device
203 Returns True or False indicating the caller to proceed with the
206 # proceed only for down operation
207 if 'down' not in ops
[0]:
210 if (ifupdownobj
.flags
.SCHED_SKIP_CHECK_UPPERIFACES
):
213 if (ifupdownflags
.flags
.FORCE
or
214 not ifupdownobj
.flags
.ADDONS_ENABLE
or
215 (not ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
) and
216 ifupdownobj
.config
.get('warn_on_ifdown', '0') == '0' and
217 not ifupdownflags
.flags
.ALL
)):
220 ulist
= ifaceobj
.upperifaces
224 # Get the list of upper ifaces other than the parent
225 tmpulist
= ([u
for u
in ulist
if u
!= parent
] if parent
229 # XXX: This is expensive. Find a cheaper way to do this.
230 # if any of the upperdevs are present,
231 # return false to the caller to skip this interface
233 if ifupdownobj
.link_exists(u
):
234 if not ifupdownflags
.flags
.ALL
:
235 if ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
):
236 ifupdownobj
.logger
.info('%s: skipping interface down,'
237 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
239 ifupdownobj
.logger
.warning('%s: skipping interface down,'
240 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
245 def run_iface_graph(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
246 order
=ifaceSchedulerFlags
.POSTORDER
,
247 followdependents
=True):
248 """ runs interface by traversing all nodes rooted at itself """
250 # Each ifacename can have a list of iface objects
251 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
253 raise Exception('%s: not found' %ifacename
)
255 # Check state of the dependent. If it is already brought up, return
256 if (cls
._STATE
_CHECK
and
257 (ifaceobjs
[0].state
== ifaceState
.from_str(ops
[-1]))):
258 ifupdownobj
.logger
.debug('%s: already processed' %ifacename
)
261 for ifaceobj
in ifaceobjs
:
262 if not cls
._check
_upperifaces
(ifupdownobj
, ifaceobj
,
263 ops
, parent
, followdependents
):
266 # If inorder, run the iface first and then its dependents
267 if order
== ifaceSchedulerFlags
.INORDER
:
268 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
270 for ifaceobj
in ifaceobjs
:
271 # Run lowerifaces or dependents
272 dlist
= ifaceobj
.lowerifaces
275 if ifaceobj
.link_kind
== ifaceLinkKind
.VRF
:
276 # remove non-auto lowerifaces from 'dlist'
277 for lower_ifname
in list(dlist
):
278 for lower_ifaceobj
in ifupdownobj
.get_ifaceobjs(lower_ifname
) or []:
279 if lower_ifaceobj
and not lower_ifaceobj
.auto
and ifaceobj
.name
== cls
.VRF_MGMT_DEVNAME
:
280 dlist
.remove(lower_ifname
)
282 ifupdownobj
.logger
.debug('%s: found dependents %s'
283 %(ifacename
, str(dlist
)))
285 if not followdependents
:
286 # XXX: this is yet another extra step,
287 # but is needed for interfaces that are
288 # implicit dependents. even though we are asked to
289 # not follow dependents, we must follow the ones
290 # that dont have user given config. Because we own them
291 new_dlist
= [d
for d
in dlist
292 if ifupdownobj
.is_iface_noconfig(d
)]
294 cls
.run_iface_list(ifupdownobj
, new_dlist
, ops
,
295 ifacename
, order
, followdependents
,
296 continueonfailure
=False)
298 cls
.run_iface_list(ifupdownobj
, dlist
, ops
,
301 continueonfailure
=False)
302 except Exception as e
:
303 if (ifupdownobj
.ignore_error(str(e
))):
306 # Dont bring the iface up if children did not come up
307 ifaceobj
.set_state_n_status(ifaceState
.NEW
,
310 if order
== ifaceSchedulerFlags
.POSTORDER
:
311 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
314 def run_iface_list(cls
, ifupdownobj
, ifacenames
,
315 ops
, parent
=None, order
=ifaceSchedulerFlags
.POSTORDER
,
316 followdependents
=True, continueonfailure
=True):
317 """ Runs interface list """
319 for ifacename
in ifacenames
:
321 cls
.run_iface_graph(ifupdownobj
, ifacename
, ops
, parent
,
322 order
, followdependents
)
323 except Exception as e
:
324 if continueonfailure
:
325 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
326 traceback
.print_tb(sys
.exc_info()[2])
327 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
330 if (ifupdownobj
.ignore_error(str(e
))):
333 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
336 def run_iface_graph_upper(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
337 followdependents
=True, skip_root
=False):
338 """ runs interface by traversing all nodes rooted at itself """
340 # Each ifacename can have a list of iface objects
341 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
343 raise Exception('%s: not found' %ifacename
)
345 if (cls
._STATE
_CHECK
and
346 (ifaceobjs
[0].state
== ifaceState
.from_str(ops
[-1]))):
347 ifupdownobj
.logger
.debug('%s: already processed' %ifacename
)
351 # run the iface first and then its upperifaces
352 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
353 for ifaceobj
in ifaceobjs
:
355 ulist
= ifaceobj
.upperifaces
357 ifupdownobj
.logger
.debug('%s: found upperifaces %s'
358 %(ifacename
, str(ulist
)))
360 cls
.run_iface_list_upper(ifupdownobj
, ulist
, ops
,
363 continueonfailure
=True)
364 except Exception as e
:
365 if (ifupdownobj
.ignore_error(str(e
))):
371 def run_iface_list_upper(cls
, ifupdownobj
, ifacenames
,
372 ops
, parent
=None, followdependents
=True,
373 continueonfailure
=True, skip_root
=False):
374 """ Runs interface list """
376 for ifacename
in ifacenames
:
378 cls
.run_iface_graph_upper(ifupdownobj
, ifacename
, ops
, parent
,
379 followdependents
, skip_root
)
380 except Exception as e
:
381 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
382 traceback
.print_tb(sys
.exc_info()[2])
383 ifupdownobj
.logger
.warning('%s : %s' %(ifacename
, str(e
)))
387 def _get_valid_upperifaces(cls
, ifupdownobj
, ifacenames
,
389 """ Recursively find valid upperifaces
391 valid upperifaces are:
392 - An upperiface which had no user config (example builtin
393 interfaces. usually vlan interfaces.)
394 - or had config and previously up
395 - and interface currently does not exist
396 - or is a bridge (because if your upperiface was a bridge
397 - u will have to execute up on the bridge
398 to enslave the port and apply bridge attributes to the port) """
401 for ifacename
in ifacenames
:
403 ifaceobj
= ifupdownobj
.get_ifaceobj_first(ifacename
)
406 ulist
= set(ifaceobj
.upperifaces
or []).difference(upperifacenames
)
409 uifaceobj
= ifupdownobj
.get_ifaceobj_first(u
)
412 has_config
= not (uifaceobj
.priv_flags
and
413 uifaceobj
.priv_flags
.NOCONFIG
)
414 if (((has_config
and ifupdownobj
.get_ifaceobjs_saved(u
)) or
415 not has_config
) and (not ifupdownobj
.link_exists(u
)
416 # Do this always for a bridge. Note that this is
417 # not done for a vlan aware bridge because,
418 # in the vlan aware bridge case, the bridge module
419 # applies the bridge port configuration on the port
420 # when up is scheduled on the port.
421 or (uifaceobj
.link_kind
== ifaceLinkKind
.BRIDGE
))):
423 upperifacenames
.extend(nulist
)
424 allupperifacenames
.extend(upperifacenames
)
426 cls
._get
_valid
_upperifaces
(ifupdownobj
, upperifacenames
,
431 def run_upperifaces(cls
, ifupdownobj
, ifacenames
, ops
,
432 continueonfailure
=True):
433 """ Run through valid upperifaces """
436 cls
._get
_valid
_upperifaces
(ifupdownobj
, ifacenames
, upperifaces
)
439 # dump valid upperifaces
440 ifupdownobj
.logger
.debug(upperifaces
)
441 for u
in upperifaces
:
443 ifaceobjs
= ifupdownobj
.get_ifaceobjs(u
)
446 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
447 except Exception as e
:
448 if continueonfailure
:
449 ifupdownobj
.logger
.warning('%s' %str
(e
))
452 def _dump_dependency_info(cls
, ifupdownobj
, ifacenames
,
453 dependency_graph
=None, indegrees
=None):
454 ifupdownobj
.logger
.info('{\n')
455 ifupdownobj
.logger
.info('\nifaceobjs:')
456 for iname
in ifacenames
:
457 iobjs
= ifupdownobj
.get_ifaceobjs(iname
)
459 iobj
.dump(ifupdownobj
.logger
)
460 if (dependency_graph
):
461 ifupdownobj
.logger
.info('\nDependency Graph:')
462 ifupdownobj
.logger
.info(dependency_graph
)
464 ifupdownobj
.logger
.info('\nIndegrees:')
465 ifupdownobj
.logger
.info(indegrees
)
466 ifupdownobj
.logger
.info('}\n')
469 def get_sorted_iface_list(cls
, ifupdownobj
, ifacenames
, ops
,
470 dependency_graph
, indegrees
=None):
471 if len(ifacenames
) == 1:
473 # Get a sorted list of all interfaces
475 indegrees
= OrderedDict()
476 for ifacename
in list(dependency_graph
.keys()):
477 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
479 #cls._dump_dependency_info(ifupdownobj, ifacenames,
480 # dependency_graph, indegrees)
482 ifacenames_all_sorted
= graph
.topological_sort_graphs_all(
483 dependency_graph
, indegrees
)
484 # if ALL was set, return all interfaces
485 if ifupdownflags
.flags
.ALL
:
486 return ifacenames_all_sorted
488 # else return ifacenames passed as argument in sorted order
489 ifacenames_sorted
= []
490 [ifacenames_sorted
.append(ifacename
)
491 for ifacename
in ifacenames_all_sorted
492 if ifacename
in ifacenames
]
493 return ifacenames_sorted
496 def sched_ifaces(cls
, ifupdownobj
, ifacenames
, ops
,
497 dependency_graph
=None, indegrees
=None,
498 order
=ifaceSchedulerFlags
.POSTORDER
,
499 followdependents
=True, skipupperifaces
=False, sort
=False):
500 """ runs interface configuration modules on interfaces passed as
501 argument. Runs topological sort on interface dependency graph.
504 **ifupdownobj** (object): ifupdownMain object
506 **ifacenames** (list): list of interface names
508 **ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
510 **dependency_graph** (dict): dependency graph in adjacency list format
513 **indegrees** (dict): indegree array of the dependency graph
515 **order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
517 **followdependents** (bool): follow dependent interfaces if true
519 **sort** (bool): sort ifacelist in the case where ALL is not set
524 # if ALL/auto interfaces are specified,
525 # - walk the dependency tree in postorder or inorder depending
527 # (This is to run interfaces correctly in order)
529 # - sort iface list if the ifaces belong to a "class"
530 # - else just run iface list in the order they were specified
532 # Run any upperifaces if available
534 followupperifaces
= False
536 skip_ifacesort
= int(ifupdownobj
.config
.get('skip_ifacesort', '0'))
537 if not skip_ifacesort
and not indegrees
:
538 indegrees
= OrderedDict()
539 for ifacename
in list(dependency_graph
.keys()):
540 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
542 if not ifupdownflags
.flags
.ALL
:
544 # If there is any interface that does not exist, maybe it
545 # is a logical interface and we have to followupperifaces
546 # when it comes up, so lets get that list.
547 if any([True for i
in ifacenames
548 if ifupdownobj
.must_follow_upperifaces(i
)]):
549 followupperifaces
= (True if
550 [i
for i
in ifacenames
551 if not ifupdownobj
.link_exists(i
)]
553 # sort interfaces only if the caller asked to sort
554 # and skip_ifacesort is not on.
555 if not skip_ifacesort
and sort
:
556 run_queue
= cls
.get_sorted_iface_list(ifupdownobj
, ifacenames
,
557 ops
, dependency_graph
, indegrees
)
558 if run_queue
and 'up' in ops
[0]:
561 # if -a is set, we pick the interfaces
562 # that have no parents and use a sorted list of those
563 if not skip_ifacesort
:
564 sorted_ifacenames
= cls
.get_sorted_iface_list(ifupdownobj
,
565 ifacenames
, ops
, dependency_graph
,
567 if sorted_ifacenames
:
568 # pick interfaces that user asked
569 # and those that dont have any dependents first
570 [run_queue
.append(ifacename
)
571 for ifacename
in sorted_ifacenames
572 if ifacename
in ifacenames
and
573 not indegrees
.get(ifacename
)]
574 ifupdownobj
.logger
.debug('graph roots (interfaces that ' +
575 'dont have dependents):' + ' %s' %str
(run_queue
))
577 ifupdownobj
.logger
.warning('interface sort returned None')
579 # If queue not present, just run interfaces that were asked by the
582 run_queue
= list(ifacenames
)
583 # if we are taking the order of interfaces as specified
584 # in the interfaces file, we should reverse the list if we
585 # want to down. This can happen if 'skip_ifacesort'
591 cls
.run_iface_list(ifupdownobj
, run_queue
, ops
,
592 parent
=None, order
=order
,
593 followdependents
=followdependents
)
594 if not cls
.get_sched_status():
597 if (not skipupperifaces
and
598 ifupdownobj
.config
.get('skip_upperifaces', '0') == '0' and
599 ((not ifupdownflags
.flags
.ALL
and followdependents
) or
600 followupperifaces
) and
602 # If user had given a set of interfaces to bring up
603 # try and execute 'up' on the upperifaces
604 ifupdownobj
.logger
.info('running upperifaces (parent interfaces) ' +
607 # upperiface bring up is best effort.
608 # eg case: if we are bringing up a bridge port
609 # this section does an 'ifup on the bridge'
610 # so that the recently up'ed bridge port gets enslaved
611 # to the bridge. But the up on the bridge may
612 # throw out more errors if the bridge is not
613 # in the correct state. Lets not surprise
614 # the user with such errors when he has
615 # only requested to bring up the bridge port.
616 cls
._STATE
_CHECK
= False
617 ifupdownflags
.flags
.IGNORE_ERRORS
= True
618 cls
.run_upperifaces(ifupdownobj
, ifacenames
, ops
)
620 ifupdownflags
.flags
.IGNORE_ERRORS
= False
621 cls
._STATE
_CHECK
= True
622 # upperiface bringup is best effort, so dont propagate errors
623 # reset scheduler status to True
624 cls
.set_sched_status(True)