]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - pkg/scheduler.py
5a7597356a31cc864af818168f6d88f518beec8e
3 # Copyright 2013. Cumulus Networks, Inc.
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 *
23 class ifaceSchedulerFlags():
27 class ifaceScheduler():
28 """ scheduler functions to schedule configuration of interfaces.
30 supports scheduling of interfaces serially in plain interface list
31 or dependency graph format.
39 def run_iface_op(cls
, ifupdownobj
, ifaceobj
, op
, cenv
):
40 """ Runs sub operation on an interface """
41 ifacename
= ifaceobj
.get_name()
43 if (cls
._STATE
_CHECK
and
44 (ifaceobj
.get_state() >= ifaceState
.from_str(op
)) and
45 (ifaceobj
.get_status() == ifaceStatus
.SUCCESS
)):
46 ifupdownobj
.logger
.debug('%s: already in state %s' %(ifacename
, op
))
49 # first run ifupdownobj handlers
50 handler
= ifupdownobj
.ops_handlers
.get(op
)
52 addr_method
= ifaceobj
.get_addr_method()
53 if not addr_method
or (addr_method
and addr_method
!= 'manual'):
54 handler(ifupdownobj
, ifaceobj
)
56 if not ifupdownobj
.ADDONS_ENABLE
: return
58 for mname
in ifupdownobj
.module_ops
.get(op
):
59 m
= ifupdownobj
.modules
.get(mname
)
63 msg
= ('%s: %s : running module %s' %(ifacename
, op
, mname
))
64 if op
== 'query-checkcurr':
65 # Dont check curr if the interface object was
67 if (ifaceobj
.priv_flags
& ifupdownobj
.NOCONFIG
):
69 ifupdownobj
.logger
.debug(msg
)
71 query_ifaceobj
=ifupdownobj
.create_n_save_ifaceobjcurr(ifaceobj
))
73 ifupdownobj
.logger
.debug(msg
)
77 ifupdownobj
.log_error(str(e
))
80 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
83 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
86 if ifupdownobj
.COMPAT_EXEC_SCRIPTS
:
87 # execute /etc/network/ scripts
88 for mname
in ifupdownobj
.script_ops
.get(op
, []):
89 ifupdownobj
.logger
.debug('%s: %s : running script %s'
90 %(ifacename
, op
, mname
))
92 ifupdownobj
.exec_command(mname
, cmdenv
=cenv
)
94 ifupdownobj
.log_error(str(e
))
97 def run_iface_ops(cls
, ifupdownobj
, ifaceobj
, ops
):
98 """ Runs all operations on an interface """
99 ifacename
= ifaceobj
.get_name()
100 # minor optimization. If operation is 'down', proceed only
101 # if interface exists in the system
102 if 'down' in ops
[0] and not ifupdownobj
.link_exists(ifacename
):
103 ifupdownobj
.logger
.info('%s: does not exist' %ifacename
)
106 if ifupdownobj
.COMPAT_EXEC_SCRIPTS
:
107 # For backward compatibility generate env variables
109 cenv
= ifupdownobj
.generate_running_env(ifaceobj
, ops
[0])
110 map(lambda op
: cls
.run_iface_op(ifupdownobj
, ifaceobj
, op
, cenv
), ops
)
111 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
113 posthookfunc(ifupdownobj
, ifaceobj
)
117 def _check_upperifaces(cls
, ifupdownobj
, ifaceobj
, ops
, parent
, followdependents
=False):
118 """ Check if conflicting upper ifaces are around and warn if required
120 Returns False if this interface needs to be skipped, else return True """
122 if 'up' in ops
[0] and followdependents
:
125 ifacename
= ifaceobj
.get_name()
126 # Deal with upperdevs first
127 ulist
= ifaceobj
.get_upperifaces()
129 tmpulist
= ([u
for u
in ulist
if u
!= parent
] if parent
134 # XXX: This is expensive. Find a cheaper way to do this
135 # if any of the upperdevs are present,
136 # dont down this interface
138 if ifupdownobj
.link_exists(u
):
139 if not ifupdownobj
.FORCE
and not ifupdownobj
.ALL
:
140 ifupdownobj
.logger
.warn('%s: ' %ifacename
+
141 ' skip interface down,' +
142 ' upperiface %s still around' %u)
144 elif 'up' in ops
[0] and not ifupdownobj
.ALL
:
145 # For 'up', just warn that there is an upperdev which is
148 if not ifupdownobj
.link_exists(u
):
149 ifupdownobj
.logger
.warn('%s: upper iface %s '
150 %(ifacename
, u
) + 'does not exist')
154 def run_iface_graph(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
155 order
=ifaceSchedulerFlags
.POSTORDER
,
156 followdependents
=True):
157 """ runs interface by traversing all nodes rooted at itself """
159 # Each ifacename can have a list of iface objects
160 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
162 raise Exception('%s: not found' %ifacename
)
164 for ifaceobj
in ifaceobjs
:
165 if not cls
._check
_upperifaces
(ifupdownobj
, ifaceobj
, ops
, parent
,
168 if order
== ifaceSchedulerFlags
.INORDER
:
169 # If inorder, run the iface first and then its dependents
170 cls
.run_iface_ops(ifupdownobj
, ifaceobj
, ops
)
172 # Run lowerifaces or dependents
173 dlist
= ifaceobj
.get_lowerifaces()
175 ifupdownobj
.logger
.debug('%s:' %ifacename
+
176 ' found dependents: %s' %str
(dlist
))
178 if not followdependents
:
179 # XXX: this is yet another extra step,
180 # but is needed for interfaces that are
181 # implicit dependents. even though we are asked to
182 # not follow dependents, we must follow the ones
183 # that dont have user given config. Because we own them
184 new_dlist
= [d
for d
in dlist
185 if ifupdownobj
.is_iface_noconfig(d
)]
187 cls
.run_iface_list(ifupdownobj
, new_dlist
, ops
,
190 continueonfailure
=False)
192 cls
.run_iface_list(ifupdownobj
, dlist
, ops
,
195 continueonfailure
=False)
197 if (ifupdownobj
.ignore_error(str(e
))):
200 # Dont bring the iface up if children did not come up
201 ifaceobj
.set_state_n_status(ifaceState
.NEW
,
204 if order
== ifaceSchedulerFlags
.POSTORDER
:
205 cls
.run_iface_ops(ifupdownobj
, ifaceobj
, ops
)
208 def run_iface_list(cls
, ifupdownobj
, ifacenames
,
209 ops
, parent
=None, order
=ifaceSchedulerFlags
.POSTORDER
,
210 followdependents
=True, continueonfailure
=True):
211 """ Runs interface list """
213 for ifacename
in ifacenames
:
215 cls
.run_iface_graph(ifupdownobj
, ifacename
, ops
, parent
,
216 order
, followdependents
)
218 if continueonfailure
:
219 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
220 traceback
.print_tb(sys
.exc_info()[2])
221 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
224 if (ifupdownobj
.ignore_error(str(e
))):
227 raise Exception('error running iface %s (%s)'
228 %(ifacename
, str(e
)))
231 def run_iface_graph_upper(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
232 followdependents
=True, skip_root
=False):
233 """ runs interface by traversing all nodes rooted at itself """
235 # Each ifacename can have a list of iface objects
236 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
238 raise Exception('%s: not found' %ifacename
)
240 for ifaceobj
in ifaceobjs
:
242 # run the iface first and then its upperifaces
243 cls
.run_iface_ops(ifupdownobj
, ifaceobj
, ops
)
246 ulist
= ifaceobj
.get_upperifaces()
248 ifupdownobj
.logger
.debug('%s:' %ifacename
+
249 ' found upperifaces: %s' %str
(ulist
))
251 cls
.run_iface_list_upper(ifupdownobj
, ulist
, ops
,
254 continueonfailure
=True)
256 if (ifupdownobj
.ignore_error(str(e
))):
262 def run_iface_list_upper(cls
, ifupdownobj
, ifacenames
,
263 ops
, parent
=None, followdependents
=True,
264 continueonfailure
=True, skip_root
=False):
265 """ Runs interface list """
267 for ifacename
in ifacenames
:
269 cls
.run_iface_graph_upper(ifupdownobj
, ifacename
, ops
, parent
,
270 followdependents
, skip_root
)
272 if continueonfailure
:
273 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
274 traceback
.print_tb(sys
.exc_info()[2])
275 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
278 if (ifupdownobj
.ignore_error(str(e
))):
281 raise Exception('error running iface %s (%s)'
282 %(ifacename
, str(e
)))
285 def sched_ifaces(cls
, ifupdownobj
, ifacenames
, ops
,
286 dependency_graph
=None, indegrees
=None,
287 order
=ifaceSchedulerFlags
.POSTORDER
,
288 followdependents
=True):
289 """ Runs iface dependeny graph by visiting all the nodes
293 ifupdownobj : ifupdown object (used for getting and updating iface
295 dependency_graph : dependency graph in adjacency list
296 format (contains more than one dependency graph)
297 ops : list of operations to perform eg ['pre-up', 'up', 'post-up']
299 indegrees : indegree array if present is used to determine roots
300 of the graphs in the dependency_graph
303 if not ifupdownobj
.ALL
or not followdependents
or len(ifacenames
) == 1:
304 cls
.run_iface_list(ifupdownobj
, ifacenames
, ops
,
305 parent
=None,order
=order
,
306 followdependents
=followdependents
)
307 if not ifupdownobj
.ALL
and followdependents
and 'up' in ops
[0]:
308 # If user had given a set of interfaces to bring up
309 # try and execute 'up' on the upperifaces
310 ifupdownobj
.logger
.info('running upperifaces if available')
311 cls
._STATE
_CHECK
= False
312 cls
.run_iface_list_upper(ifupdownobj
, ifacenames
, ops
,
314 cls
._STATE
_CHECK
= True
318 # Get a sorted list of all interfaces
320 indegrees
= OrderedDict()
321 for ifacename
in dependency_graph
.keys():
322 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
323 sorted_ifacenames
= graph
.topological_sort_graphs_all(dependency_graph
,
325 ifupdownobj
.logger
.debug('sorted ifacenames %s : '
326 %str
(sorted_ifacenames
))
328 # From the sorted list, pick interfaces that user asked
329 # and those that dont have any dependents first
330 [run_queue
.append(ifacename
)
331 for ifacename
in sorted_ifacenames
332 if ifacename
in ifacenames
and
333 not indegrees
.get(ifacename
)]
335 ifupdownobj
.logger
.debug('graph roots (interfaces that dont have '
336 'dependents):' + ' %s' %str
(run_queue
))
337 cls
.run_iface_list(ifupdownobj
, run_queue
, ops
,
338 parent
=None,order
=order
,
339 followdependents
=followdependents
)
342 def run_iface(cls
, ifupdownobj
, ifacename
, ops
):
343 """ Runs operation on an interface """
345 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
347 cls
.run_iface_ops(ifupdownobj
, i
, ops
)
350 def run_iface_list_op(cls
, ifupdownobj
, ifacenames
, op
,
351 sorted_by_dependency
=False):
352 """ Runs interface list through sub operation handler. """
354 ifupdownobj
.logger
.debug('running operation %s on all given interfaces'
356 iface_run_queue
= deque(ifacenames
)
357 for i
in range(0, len(iface_run_queue
)):
358 if op
.endswith('up'):
360 if sorted_by_dependency
:
361 ifacename
= iface_run_queue
.pop()
363 ifacename
= iface_run_queue
.popleft()
365 if sorted_by_dependency
:
366 ifacename
= iface_run_queue
.popleft()
368 ifacename
= iface_run_queue
.pop()
371 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
372 for ifaceobj
in ifaceobjs
:
373 cenv
= ifupdownobj
.generate_running_env(ifaceobj
, op
)
374 cls
.run_iface_op(ifupdownobj
, ifaceobj
, op
, cenv
)
376 ifupdownobj
.log_error(str(e
))
379 def run_iface_list_ops(cls
, ifupdownobj
, ifacenames
, ops
,
380 sorted_by_dependency
=False):
381 """ Runs interface list through sub operations handler
383 Unlike run_iface_list, this method executes a sub operation on the
384 entire interface list before proceeding to the next sub-operation.
385 ie operation 'pre-up' is run through the entire interface list before
388 # Each sub operation has a module list
389 [cls
.run_iface_list_op(ifupdownobj
, ifacenames
, op
,
390 sorted_by_dependency
) for op
in ops
]
393 def run_iface_dependency_graphs_sorted(cls
, ifupdownobj
,
397 """ runs interface dependency graph by topologically sorting the interfaces """
399 if indegrees
is None:
400 indegrees
= OrderedDict()
401 for ifacename
in dependency_graphs
.keys():
402 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
404 ifupdownobj
.logger
.debug('indegree array :')
405 ifupdownobj
.logger
.debug(ifupdownobj
.pp
.pformat(indegrees
))
408 ifupdownobj
.logger
.debug('calling topological sort on the graph ' +
411 sorted_ifacenames
= graph
.topological_sort_graphs_all(
412 dependency_graphs
, indegrees
)
414 sorted_ifacenames
= graph
.topological_sort_graphs(
415 dependency_graphs
, indegrees
)
419 ifupdownobj
.logger
.debug('sorted iface list = %s' %sorted
_ifacenames
)
420 cls
.run_iface_list_ops(ifupdownobj
, sorted_ifacenames
, ops
,
421 sorted_by_dependency
=True)
424 """ Methods to execute interfaces in parallel """
426 def init_tokens(cls
, count
):
427 cls
.token_pool
= BoundedSemaphore(count
)
430 def accquire_token(cls
, logprefix
=''):
431 cls
.token_pool
.acquire()
434 def release_token(cls
, logprefix
=''):
435 cls
.token_pool
.release()
438 def run_iface_parallel(cls
, ifupdownobj
, ifacename
, op
):
439 """ Configures interface in parallel.
441 Executes all its direct dependents in parallel
445 ifupdownobj
.logger
.debug('%s:' %ifacename
+ ' %s' %op
)
446 cls
.accquire_token(iface
)
448 # Each iface can have a list of objects
449 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
450 if ifaceobjs
is None:
451 ifupdownobj
.logger
.warning('%s: ' %ifacename
+ 'not found')
452 cls
.release_token(ifacename
)
455 for ifaceobj
in ifaceobjs
:
457 dlist
= ifaceobj
.get_lowerifaces()
459 ifupdownobj
.logger
.debug('%s:' %ifacename
+
460 ' found dependents: %s' %str
(dlist
))
462 cls
.release_token(ifacename
)
463 cls
.run_iface_list_parallel(ifacename
, ifupdownobj
,
465 cls
.accquire_token(ifacename
)
467 if ifupdownobj
.ignore_error(str(e
)):
470 # Dont bring the iface up if children did not come up
471 ifupdownobj
.logger
.debug('%s:' %ifacename
+
472 ' there was an error bringing %s' %op
+
473 ' dependents (%s)', str(e
))
474 ifupdownobj
.set_iface_state(ifaceobj
,
475 ifaceState
.from_str(ops
[0]),
479 # Run all sub operations sequentially
481 ifupdownobj
.logger
.debug('%s:' %ifacename
+
482 ' running sub-operations')
483 cls
.run_iface_ops(ifupdownobj
, ifaceobj
, op
)
485 ifupdownobj
.logger
.error('%s:' %ifacename
+
486 ' error running sub operations (%s)' %str
(e
))
488 cls
.release_token(ifacename
)
491 def run_iface_list_parallel(cls
, parent
, ifupdownobj
, ifacenames
, op
):
492 """ Runs interface list in parallel """
494 running_threads
= OrderedDict()
497 for ifacename
in ifacenames
:
499 cls
.accquire_token(parent
)
500 running_threads
[ifacename
] = Thread(None,
501 cls
.run_iface_parallel
, ifacename
,
502 args
=(ifupdownobj
, ifacename
, op
))
503 running_threads
[ifacename
].start()
504 cls
.release_token(parent
)
506 cls
.release_token(parent
)
507 if ifupdownobj
.ignore_error(str(e
)):
510 raise Exception('error starting thread for iface %s'
514 ifupdownobj
.logger
.debug('%s ' %parent
+
515 'waiting for all the threads ...')
516 for ifacename
, t
in running_threads
.items():
518 if ifupdownobj
.get_iface_status(ifacename
) != ifaceStatus
.SUCCESS
:
524 def run_iface_graphs_parallel(cls
, parent
, ifupdownobj
, ifacenames
, op
):
525 """ Runs iface graphs in parallel """
527 running_threads
= OrderedDict()
530 for ifacename
in ifacenames
:
532 cls
.accquire_graph_token(parent
)
533 running_threads
[ifacename
] = Thread(None,
534 cls
.run_iface_parallel
, ifacename
,
535 args
=(ifupdownobj
, ifacename
, op
))
536 running_threads
[ifacename
].start()
537 cls
.release_graph_token(parent
)
539 cls
.release_graph_token(parent
)
540 if ifupdownobj
.ignore_error(str(e
)):
543 raise Exception('error starting thread for iface %s'
546 ifupdownobj
.logger
.info('%s ' %parent
+
547 'waiting for all the threads ...')
548 for ifacename
, t
in running_threads
.items():
550 # Check status of thread
551 # XXX: Check all objs
552 if ifupdownobj
.get_iface_status(ifacename
) != ifaceStatus
.SUCCESS
:
557 def run_iface_dependency_graph_parallel(cls
, ifupdownobj
, dependency_graph
,
559 """ Runs iface dependeny graph in parallel.
562 ifupdownobj -- ifupdown object (used for getting and updating iface
564 dependency_graph -- dependency graph with
565 operation -- 'up' or 'down' or 'query'
569 ifupdownobj
.logger
.debug('running dependency graph in parallel ..')
571 # Build a list of ifaces that dont have any dependencies
572 for ifacename
in dependency_graph
.keys():
573 if ifupdownobj
.get_iface_refcnt(ifacename
) == 0:
574 run_queue
.append(ifacename
)
576 ifupdownobj
.logger
.debug('graph roots (interfaces that dont'
577 ' have dependents):' + ' %s' %str
(run_queue
))
578 cls
.init_tokens(ifupdownobj
.get_njobs())
579 return cls
.run_iface_list_parallel('main', ifupdownobj
, run_queue
,
583 # Run one graph at a time
584 #for iface in run_queue:
585 # self.run_iface_list_parallel('main', ifupdownobj, [iface],