]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/ifupdown/scheduler.py
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
10 from statemanager
import *
13 from collections
import deque
14 from collections
import OrderedDict
19 from collections
import deque
20 from threading
import *
21 from ifupdownbase
import *
24 class ifaceSchedulerFlags():
25 """ Enumerates scheduler flags """
30 class ifaceScheduler():
31 """ scheduler functions to schedule configuration of interfaces.
33 supports scheduling of interfaces serially in plain interface list
34 or dependency graph format.
43 def run_iface_op(cls
, ifupdownobj
, ifaceobj
, op
, cenv
=None):
44 """ Runs sub operation on an interface """
45 ifacename
= ifaceobj
.name
47 if ifupdownobj
.type and ifupdownobj
.type != ifaceobj
.type:
50 if not ifupdownobj
.ADDONS_ENABLE
: return
51 if op
== 'query-checkcurr':
52 query_ifaceobj
=ifupdownobj
.create_n_save_ifaceobjcurr(ifaceobj
)
53 # If not type bridge vlan and the object does not exist,
54 # mark not found and return
55 if (not ifupdownobj
.link_exists(ifaceobj
.name
) and
56 ifaceobj
.type != ifaceType
.BRIDGE_VLAN
):
57 query_ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
60 for mname
in ifupdownobj
.module_ops
.get(op
):
61 m
= ifupdownobj
.modules
.get(mname
)
65 msg
= ('%s: %s : running module %s' %(ifacename
, op
, mname
))
66 if op
== 'query-checkcurr':
67 # Dont check curr if the interface object was
69 if (ifaceobj
.priv_flags
& ifupdownobj
.NOCONFIG
):
71 ifupdownobj
.logger
.debug(msg
)
72 m
.run(ifaceobj
, op
, query_ifaceobj
,
73 ifaceobj_getfunc
=ifupdownobj
.get_ifaceobjs
)
75 ifupdownobj
.logger
.debug(msg
)
77 ifaceobj_getfunc
=ifupdownobj
.get_ifaceobjs
)
79 if not ifupdownobj
.ignore_error(str(e
)):
81 ifupdownobj
.logger
.warn(str(e
))
82 # Continue with rest of the modules
85 if err
or ifaceobj
.status
== ifaceStatus
.ERROR
:
86 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
88 if 'up' in op
or 'down' in op
:
89 cls
._SCHED
_RETVAL
= False
91 # Mark success only if the interface was not already
93 status
= (ifaceobj
.status
94 if ifaceobj
.status
== ifaceStatus
.ERROR
95 else ifaceStatus
.SUCCESS
)
96 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
99 if ifupdownobj
.config
.get('addon_scripts_support', '0') == '1':
100 # execute /etc/network/ scripts
101 for mname
in ifupdownobj
.script_ops
.get(op
, []):
102 ifupdownobj
.logger
.debug('%s: %s : running script %s'
103 %(ifacename
, op
, mname
))
105 ifupdownobj
.exec_command(mname
, cmdenv
=cenv
)
107 ifupdownobj
.log_error(str(e
))
110 def run_iface_list_ops(cls
, ifupdownobj
, ifaceobjs
, ops
):
111 """ Runs all operations on a list of interface
112 configurations for the same interface
115 # minor optimization. If operation is 'down', proceed only
116 # if interface exists in the system
117 ifacename
= ifaceobjs
[0].name
118 ifupdownobj
.logger
.info('%s: running ops ...' %ifacename
)
119 if ('down' in ops
[0] and
120 ifaceobjs
[0].type != ifaceType
.BRIDGE_VLAN
and
121 not ifupdownobj
.link_exists(ifacename
)):
122 ifupdownobj
.logger
.debug('%s: does not exist' %ifacename
)
123 # run posthook before you get out of here, so that
124 # appropriate cleanup is done
125 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
127 for ifaceobj
in ifaceobjs
:
128 ifaceobj
.status
= ifaceStatus
.SUCCESS
129 posthookfunc(ifupdownobj
, ifaceobj
, 'down')
132 # first run ifupdownobj handlers. This is good enough
133 # for the first object in the list
134 handler
= ifupdownobj
.ops_handlers
.get(op
)
137 handler(ifupdownobj
, ifaceobjs
[0])
139 if not ifupdownobj
.link_master_slave_ignore_error(str(e
)):
140 ifupdownobj
.logger
.warn('%s: %s'
141 %(ifaceobjs
[0].name
, str(e
)))
143 for ifaceobj
in ifaceobjs
:
144 cls
.run_iface_op(ifupdownobj
, ifaceobj
, op
,
145 cenv
=ifupdownobj
.generate_running_env(ifaceobj
, op
)
146 if ifupdownobj
.config
.get('addon_scripts_support',
147 '0') == '1' else None)
148 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
151 [posthookfunc(ifupdownobj
, ifaceobj
, ops
[0])
152 for ifaceobj
in ifaceobjs
]
154 ifupdownobj
.logger
.warn('%s' %str
(e
))
158 def _check_upperifaces(cls
, ifupdownobj
, ifaceobj
, ops
, parent
,
159 followdependents
=False):
160 """ Check if upperifaces are hanging off us and help caller decide
161 if he can proceed with the ops on this device
163 Returns True or False indicating the caller to proceed with the
166 # proceed only for down operation
167 if 'down' not in ops
[0]:
170 if (ifupdownobj
.FORCE
or
171 not ifupdownobj
.ADDONS_ENABLE
or
172 (not ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
) and
173 ifupdownobj
.config
.get('warn_on_ifdown', '0') == '0' and
174 not ifupdownobj
.ALL
)):
177 ulist
= ifaceobj
.upperifaces
181 # Get the list of upper ifaces other than the parent
182 tmpulist
= ([u
for u
in ulist
if u
!= parent
] if parent
186 # XXX: This is expensive. Find a cheaper way to do this.
187 # if any of the upperdevs are present,
188 # return false to the caller to skip this interface
190 if ifupdownobj
.link_exists(u
):
191 if not ifupdownobj
.ALL
:
192 if ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
):
193 ifupdownobj
.logger
.info('%s: skipping interface down,'
194 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
196 ifupdownobj
.logger
.warn('%s: skipping interface down,'
197 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
202 def run_iface_graph(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
203 order
=ifaceSchedulerFlags
.POSTORDER
,
204 followdependents
=True):
205 """ runs interface by traversing all nodes rooted at itself """
207 # Each ifacename can have a list of iface objects
208 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
210 raise Exception('%s: not found' %ifacename
)
212 # Check state of the dependent. If it is already brought up, return
213 if (cls
._STATE
_CHECK
and
214 (ifaceobjs
[0].state
== ifaceState
.from_str(ops
[-1]))):
215 ifupdownobj
.logger
.debug('%s: already processed' %ifacename
)
218 for ifaceobj
in ifaceobjs
:
219 if not cls
._check
_upperifaces
(ifupdownobj
, ifaceobj
,
220 ops
, parent
, followdependents
):
223 # If inorder, run the iface first and then its dependents
224 if order
== ifaceSchedulerFlags
.INORDER
:
225 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
227 for ifaceobj
in ifaceobjs
:
228 # Run lowerifaces or dependents
229 dlist
= ifaceobj
.lowerifaces
231 ifupdownobj
.logger
.debug('%s: found dependents %s'
232 %(ifacename
, str(dlist
)))
234 if not followdependents
:
235 # XXX: this is yet another extra step,
236 # but is needed for interfaces that are
237 # implicit dependents. even though we are asked to
238 # not follow dependents, we must follow the ones
239 # that dont have user given config. Because we own them
240 new_dlist
= [d
for d
in dlist
241 if ifupdownobj
.is_iface_noconfig(d
)]
243 cls
.run_iface_list(ifupdownobj
, new_dlist
, ops
,
244 ifacename
, order
, followdependents
,
245 continueonfailure
=False)
247 cls
.run_iface_list(ifupdownobj
, dlist
, ops
,
250 continueonfailure
=False)
252 if (ifupdownobj
.ignore_error(str(e
))):
255 # Dont bring the iface up if children did not come up
256 ifaceobj
.set_state_n_status(ifaceState
.NEW
,
259 if order
== ifaceSchedulerFlags
.POSTORDER
:
260 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
263 def run_iface_list(cls
, ifupdownobj
, ifacenames
,
264 ops
, parent
=None, order
=ifaceSchedulerFlags
.POSTORDER
,
265 followdependents
=True, continueonfailure
=True):
266 """ Runs interface list """
268 for ifacename
in ifacenames
:
270 cls
.run_iface_graph(ifupdownobj
, ifacename
, ops
, parent
,
271 order
, followdependents
)
273 if continueonfailure
:
274 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
275 traceback
.print_tb(sys
.exc_info()[2])
276 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
279 if (ifupdownobj
.ignore_error(str(e
))):
282 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
285 def run_iface_graph_upper(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
286 followdependents
=True, skip_root
=False):
287 """ runs interface by traversing all nodes rooted at itself """
289 # Each ifacename can have a list of iface objects
290 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
292 raise Exception('%s: not found' %ifacename
)
294 if (cls
._STATE
_CHECK
and
295 (ifaceobjs
[0].state
== ifaceState
.from_str(ops
[-1]))):
296 ifupdownobj
.logger
.debug('%s: already processed' %ifacename
)
300 # run the iface first and then its upperifaces
301 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
302 for ifaceobj
in ifaceobjs
:
304 ulist
= ifaceobj
.upperifaces
306 ifupdownobj
.logger
.debug('%s: found upperifaces %s'
307 %(ifacename
, str(ulist
)))
309 cls
.run_iface_list_upper(ifupdownobj
, ulist
, ops
,
312 continueonfailure
=True)
314 if (ifupdownobj
.ignore_error(str(e
))):
320 def run_iface_list_upper(cls
, ifupdownobj
, ifacenames
,
321 ops
, parent
=None, followdependents
=True,
322 continueonfailure
=True, skip_root
=False):
323 """ Runs interface list """
325 for ifacename
in ifacenames
:
327 cls
.run_iface_graph_upper(ifupdownobj
, ifacename
, ops
, parent
,
328 followdependents
, skip_root
)
330 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
331 traceback
.print_tb(sys
.exc_info()[2])
332 ifupdownobj
.logger
.warn('%s : %s' %(ifacename
, str(e
)))
336 def _get_valid_upperifaces(cls
, ifupdownobj
, ifacenames
,
338 """ Recursively find valid upperifaces
340 valid upperifaces are:
341 - An upperiface which had no user config (example builtin
342 interfaces. usually vlan interfaces.)
343 - or had config and previously up
344 - and interface currently does not exist
345 - or is a bridge (because if your upperiface was a bridge
346 - u will have to execute up on the bridge
347 to enslave the port and apply bridge attributes to the port) """
350 for ifacename
in ifacenames
:
352 ifaceobj
= ifupdownobj
.get_ifaceobj_first(ifacename
)
355 ulist
= Set(ifaceobj
.upperifaces
).difference(upperifacenames
)
358 uifaceobj
= ifupdownobj
.get_ifaceobj_first(u
)
361 has_config
= not bool(uifaceobj
.priv_flags
362 & ifupdownobj
.NOCONFIG
)
363 if (((has_config
and ifupdownobj
.get_ifaceobjs_saved(u
)) or
364 not has_config
) and (not ifupdownobj
.link_exists(u
)
365 or uifaceobj
.link_kind
== ifaceLinkKind
.BRIDGE
)):
367 upperifacenames
.extend(nulist
)
368 allupperifacenames
.extend(upperifacenames
)
370 cls
._get
_valid
_upperifaces
(ifupdownobj
, upperifacenames
,
375 def run_upperifaces(cls
, ifupdownobj
, ifacenames
, ops
,
376 continueonfailure
=True):
377 """ Run through valid upperifaces """
380 cls
._get
_valid
_upperifaces
(ifupdownobj
, ifacenames
, upperifaces
)
383 # dump valid upperifaces
384 ifupdownobj
.logger
.debug(upperifaces
)
385 for u
in upperifaces
:
387 ifaceobjs
= ifupdownobj
.get_ifaceobjs(u
)
390 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
392 if continueonfailure
:
393 self
.logger
.warn('%s' %str
(e
))
396 def get_sorted_iface_list(cls
, ifupdownobj
, ifacenames
, ops
,
397 dependency_graph
, indegrees
=None):
398 if len(ifacenames
) == 1:
400 # Get a sorted list of all interfaces
402 indegrees
= OrderedDict()
403 for ifacename
in dependency_graph
.keys():
404 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
405 ifacenames_all_sorted
= graph
.topological_sort_graphs_all(
406 dependency_graph
, indegrees
)
407 # if ALL was set, return all interfaces
409 return ifacenames_all_sorted
411 # else return ifacenames passed as argument in sorted order
412 ifacenames_sorted
= []
413 [ifacenames_sorted
.append(ifacename
)
414 for ifacename
in ifacenames_all_sorted
415 if ifacename
in ifacenames
]
416 return ifacenames_sorted
419 def sched_ifaces(cls
, ifupdownobj
, ifacenames
, ops
,
420 dependency_graph
=None, indegrees
=None,
421 order
=ifaceSchedulerFlags
.POSTORDER
,
422 followdependents
=True, skipupperifaces
=False, sort
=False):
423 """ runs interface configuration modules on interfaces passed as
424 argument. Runs topological sort on interface dependency graph.
427 **ifupdownobj** (object): ifupdownMain object
429 **ifacenames** (list): list of interface names
431 **ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
433 **dependency_graph** (dict): dependency graph in adjacency list format
436 **indegrees** (dict): indegree array of the dependency graph
438 **order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
440 **followdependents** (bool): follow dependent interfaces if true
442 **sort** (bool): sort ifacelist in the case where ALL is not set
447 # if ALL/auto interfaces are specified,
448 # - walk the dependency tree in postorder or inorder depending
450 # (This is to run interfaces correctly in order)
452 # - sort iface list if the ifaces belong to a "class"
453 # - else just run iface list in the order they were specified
455 # Run any upperifaces if available
457 followupperifaces
= False
459 skip_ifacesort
= int(ifupdownobj
.config
.get('skip_ifacesort', '0'))
460 if not skip_ifacesort
and not indegrees
:
461 indegrees
= OrderedDict()
462 for ifacename
in dependency_graph
.keys():
463 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
465 if not ifupdownobj
.ALL
:
467 # If there is any interface that does not exist, maybe it
468 # is a logical interface and we have to followupperifaces
469 # when it comes up, so lets get that list.
470 if any([True for i
in ifacenames
471 if ifupdownobj
.must_follow_upperifaces(i
)]):
472 followupperifaces
= (True if
473 [i
for i
in ifacenames
474 if not ifupdownobj
.link_exists(i
)]
476 # sort interfaces only if the caller asked to sort
477 # and skip_ifacesort is not on.
478 if not skip_ifacesort
and sort
:
479 run_queue
= cls
.get_sorted_iface_list(ifupdownobj
, ifacenames
,
480 ops
, dependency_graph
, indegrees
)
481 if run_queue
and 'up' in ops
[0]:
484 # if -a is set, we pick the interfaces
485 # that have no parents and use a sorted list of those
486 if not skip_ifacesort
:
487 sorted_ifacenames
= cls
.get_sorted_iface_list(ifupdownobj
,
488 ifacenames
, ops
, dependency_graph
,
490 if sorted_ifacenames
:
491 # pick interfaces that user asked
492 # and those that dont have any dependents first
493 [run_queue
.append(ifacename
)
494 for ifacename
in sorted_ifacenames
495 if ifacename
in ifacenames
and
496 not indegrees
.get(ifacename
)]
497 ifupdownobj
.logger
.debug('graph roots (interfaces that ' +
498 'dont have dependents):' + ' %s' %str
(run_queue
))
500 ifupdownobj
.logger
.warn('interface sort returned None')
502 # If queue not present, just run interfaces that were asked by the
505 run_queue
= list(ifacenames
)
506 # if we are taking the order of interfaces as specified
507 # in the interfaces file, we should reverse the list if we
508 # want to down. This can happen if 'skip_ifacesort'
514 cls
.run_iface_list(ifupdownobj
, run_queue
, ops
,
515 parent
=None, order
=order
,
516 followdependents
=followdependents
)
517 if not cls
._SCHED
_RETVAL
:
520 if (not skipupperifaces
and
521 ifupdownobj
.config
.get('skip_upperifaces', '0') == '0' and
522 ((not ifupdownobj
.ALL
and followdependents
) or
523 followupperifaces
) and
525 # If user had given a set of interfaces to bring up
526 # try and execute 'up' on the upperifaces
527 ifupdownobj
.logger
.info('running upperifaces (parent interfaces) ' +
529 cls
._STATE
_CHECK
= False
530 cls
.run_upperifaces(ifupdownobj
, ifacenames
, ops
)
531 cls
._STATE
_CHECK
= True