]> git.proxmox.com Git - mirror_ifupdown2.git/blame - pkg/scheduler.py
ifudown --no-scripts support
[mirror_ifupdown2.git] / pkg / scheduler.py
CommitLineData
a6f80f0e 1#!/usr/bin/python
3e8ee54f 2#
3# Copyright 2013. Cumulus Networks, Inc.
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6# ifaceScheduler --
7# interface scheduler
8#
a6f80f0e 9
a6f80f0e 10from statemanager import *
11from iface import *
12from graph import *
13from collections import deque
14from collections import OrderedDict
a6f80f0e 15import logging
d08d5f54 16import traceback
69f58278 17import sys
a6f80f0e 18from graph import *
19from collections import deque
20from threading import *
21from ifupdownbase import *
22
d08d5f54 23class ifaceSchedulerFlags():
24 INORDER = 1
25 POSTORDER = 2
26
be0b20f2 27class ifaceScheduler():
28 """ scheduler functions to schedule configuration of interfaces.
a6f80f0e 29
30
31 supports scheduling of interfaces serially in plain interface list
32 or dependency graph format.
33 """
34
be0b20f2 35 token_pool = None
d08d5f54 36
be0b20f2 37 @classmethod
38 def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv):
a6f80f0e 39 """ Runs sub operation on an interface """
d08d5f54 40 ifacename = ifaceobj.get_name()
a6f80f0e 41
d08d5f54 42 if (ifaceobj.get_state() >= ifaceState.from_str(op) and
43 ifaceobj.get_status() == ifaceStatus.SUCCESS):
be0b20f2 44 ifupdownobj.logger.debug('%s: already in state %s' %(ifacename, op))
d08d5f54 45 return
a6f80f0e 46
be0b20f2 47 # first run ifupdownobj handlers
48 handler = ifupdownobj.ops_handlers.get(op)
49 if handler:
50 addr_method = ifaceobj.get_addr_method()
51 if not addr_method or (addr_method and addr_method != 'manual'):
52 handler(ifupdownobj, ifaceobj)
53
20dd6242 54 if not ifupdownobj.ADDONS_ENABLE: return
55
be0b20f2 56 for mname in ifupdownobj.module_ops.get(op):
37c0543d 57 m = ifupdownobj.modules.get(mname)
a6f80f0e 58 err = 0
59 try:
d08d5f54 60 if hasattr(m, 'run'):
be0b20f2 61 ifupdownobj.logger.debug('%s: %s : running module %s'
d08d5f54 62 %(ifacename, op, mname))
739f665b 63 if op == 'query-checkcurr':
d08d5f54 64 # Dont check curr if the interface object was
37c0543d 65 # auto generated
d08d5f54 66 if (ifaceobj.priv_flags & ifupdownobj.NOCONFIG):
37c0543d 67 continue
d08d5f54 68 m.run(ifaceobj, op,
69 query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj))
a6f80f0e 70 else:
d08d5f54 71 m.run(ifaceobj, op)
a6f80f0e 72 except Exception, e:
73 err = 1
be0b20f2 74 ifupdownobj.log_error(str(e))
a6f80f0e 75 finally:
31a5f4c3 76 if err:
77 ifaceobj.set_state_n_status(ifaceState.from_str(op),
78 ifaceStatus.ERROR)
d08d5f54 79 else:
31a5f4c3 80 ifaceobj.set_state_n_status(ifaceState.from_str(op),
81 ifaceStatus.SUCCESS)
6bd7fc74 82
83 if ifupdownobj.COMPAT_EXEC_SCRIPTS:
84 # execute /etc/network/ scripts
85 for mname in ifupdownobj.script_ops.get(op, []):
86 ifupdownobj.logger.debug('%s: %s : running script %s'
d08d5f54 87 %(ifacename, op, mname))
6bd7fc74 88 try:
89 ifupdownobj.exec_command(mname, cmdenv=cenv)
90 except Exception, e:
91 ifupdownobj.log_error(str(e))
37c0543d 92
be0b20f2 93 @classmethod
94 def run_iface_ops(cls, ifupdownobj, ifaceobj, ops):
31a5f4c3 95 """ Runs all operations on an interface """
6bd7fc74 96 cenv=None
6bd7fc74 97 if ifupdownobj.COMPAT_EXEC_SCRIPTS:
98 # For backward compatibility generate env variables
99 # for attributes
100 cenv = ifupdownobj.generate_running_env(ifaceobj, ops[0])
6bd7fc74 101 map(lambda op: cls.run_iface_op(ifupdownobj, ifaceobj, op, cenv), ops)
31a5f4c3 102 posthookfunc = ifupdownobj.sched_hooks.get('posthook')
103 if posthookfunc:
104 posthookfunc(ifupdownobj, ifaceobj)
a6f80f0e 105
be0b20f2 106 @classmethod
107 def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
d08d5f54 108 order=ifaceSchedulerFlags.POSTORDER,
109 followdependents=True):
6ef5bfa2 110 """ runs interface by traversing all nodes rooted at itself """
111
112 # minor optimization. If operation is 'down', proceed only
113 # if interface exists in the system
be0b20f2 114 if 'down' in ops[0] and not ifupdownobj.link_exists(ifacename):
115 ifupdownobj.logger.info('%s: does not exist' %ifacename)
6ef5bfa2 116 return
a6f80f0e 117
d08d5f54 118 # Each ifacename can have a list of iface objects
31a5f4c3 119 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
120 if not ifaceobjs:
d08d5f54 121 raise Exception('%s: not found' %ifacename)
a6f80f0e 122
d08d5f54 123 for ifaceobj in ifaceobjs:
f3215127 124 # Deal with upperdevs first
125 ulist = ifaceobj.get_upperifaces()
126 if ulist:
f3215127 127 tmpulist = ([u for u in ulist if u != parent] if parent
128 else ulist)
129 if tmpulist:
f3215127 130 if 'down' in ops[0]:
131 # XXX: This is expensive. Find a cheaper way to do this
132 # if any of the upperdevs are present,
133 # dont down this interface
134 for u in tmpulist:
be0b20f2 135 if ifupdownobj.link_exists(u):
f3215127 136 if not ifupdownobj.ALL:
be0b20f2 137 ifupdownobj.logger.warn('%s: ' %ifacename +
138 ' skip interface down,' +
139 ' upperiface %s still around' %u)
f3215127 140 return
141 elif 'up' in ops[0] and not ifupdownobj.ALL:
142 # For 'up', just warn that there is an upperdev which is
143 # probably not up
144 for u in tmpulist:
be0b20f2 145 if not ifupdownobj.link_exists(u):
146 ifupdownobj.logger.warn('%s: upper iface %s '
147 %(ifacename, u) + 'does not exist')
f3215127 148
d08d5f54 149 if order == ifaceSchedulerFlags.INORDER:
f3215127 150 # If inorder, run the iface first and then its dependents
be0b20f2 151 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
f3215127 152
153 # Run lowerifaces or dependents
154 dlist = ifaceobj.get_lowerifaces()
155 if dlist:
20dd6242 156 ifupdownobj.logger.debug('%s:' %ifacename +
d08d5f54 157 ' found dependents: %s' %str(dlist))
158 try:
159 if not followdependents:
160 # XXX: this is yet another extra step,
161 # but is needed for interfaces that are
f3215127 162 # implicit dependents. even though we are asked to
163 # not follow dependents, we must follow the ones
164 # that dont have user given config. Because we own them
d08d5f54 165 new_dlist = [d for d in dlist
166 if ifupdownobj.is_iface_noconfig(d)]
6ef5bfa2 167 if new_dlist:
be0b20f2 168 cls.run_iface_list(ifupdownobj, new_dlist, ops,
f3215127 169 ifacename, order,
170 followdependents,
6ef5bfa2 171 continueonfailure=False)
d08d5f54 172 else:
be0b20f2 173 cls.run_iface_list(ifupdownobj, dlist, ops,
f3215127 174 ifacename, order,
175 followdependents,
6ef5bfa2 176 continueonfailure=False)
d08d5f54 177 except Exception, e:
be0b20f2 178 if (ifupdownobj.ignore_error(str(e))):
d08d5f54 179 pass
180 else:
181 # Dont bring the iface up if children did not come up
31a5f4c3 182 ifaceobj.set_state_n_sttaus(ifaceState.NEW,
183 ifacestatus.ERROR)
d08d5f54 184 raise
d08d5f54 185 if order == ifaceSchedulerFlags.POSTORDER:
be0b20f2 186 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
a6f80f0e 187
be0b20f2 188 @classmethod
189 def run_iface_list(cls, ifupdownobj, ifacenames,
f3215127 190 ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
6ef5bfa2 191 followdependents=True, continueonfailure=True):
d08d5f54 192 """ Runs interface list """
a6f80f0e 193
d08d5f54 194 for ifacename in ifacenames:
a6f80f0e 195 try:
be0b20f2 196 cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
d08d5f54 197 order, followdependents)
a6f80f0e 198 except Exception, e:
6ef5bfa2 199 if continueonfailure:
69f58278 200 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
201 traceback.print_tb(sys.exc_info()[2])
be0b20f2 202 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
d08d5f54 203 pass
204 else:
be0b20f2 205 if (ifupdownobj.ignore_error(str(e))):
6ef5bfa2 206 pass
207 else:
208 raise Exception('error running iface %s (%s)'
209 %(ifacename, str(e)))
d08d5f54 210
be0b20f2 211 @classmethod
212 def run_iface_dependency_graphs(cls, ifupdownobj,
d08d5f54 213 dependency_graph, ops, indegrees=None,
214 order=ifaceSchedulerFlags.POSTORDER,
215 followdependents=True):
216 """ Runs iface dependeny graph by visiting all the nodes
217
218 Parameters:
219 -----------
220 ifupdownobj : ifupdown object (used for getting and updating iface
221 object state)
222 dependency_graph : dependency graph in adjacency list
223 format (contains more than one dependency graph)
224 ops : list of operations to perform eg ['pre-up', 'up', 'post-up']
225
226 indegrees : indegree array if present is used to determine roots
227 of the graphs in the dependency_graph
228 """
d08d5f54 229 run_queue = []
f3215127 230
20dd6242 231 if not indegrees:
f3215127 232 indegrees = OrderedDict()
d08d5f54 233 for ifacename in dependency_graph.keys():
f3215127 234 indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
235
236 sorted_ifacenames = graph.topological_sort_graphs_all(dependency_graph,
237 dict(indegrees))
be0b20f2 238 ifupdownobj.logger.debug('sorted ifacenames %s : '
239 %str(sorted_ifacenames))
f3215127 240
241 # Build a list of ifaces that dont have any dependencies
242 for ifacename in sorted_ifacenames:
243 if not indegrees.get(ifacename):
244 run_queue.append(ifacename)
d08d5f54 245
20dd6242 246 ifupdownobj.logger.debug('graph roots (interfaces that dont have '
247 'dependents):' + ' %s' %str(run_queue))
d08d5f54 248
be0b20f2 249 return cls.run_iface_list(ifupdownobj, run_queue, ops,
250 parent=None,order=order,
251 followdependents=followdependents)
d08d5f54 252
be0b20f2 253 @classmethod
254 def run_iface(cls, ifupdownobj, ifacename, ops):
d08d5f54 255 """ Runs operation on an interface """
256
31a5f4c3 257 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
d08d5f54 258 for i in ifaceobjs:
be0b20f2 259 cls.run_iface_ops(ifupdownobj, i, ops)
d08d5f54 260
be0b20f2 261 @classmethod
262 def run_iface_list_op(cls, ifupdownobj, ifacenames, op,
263 sorted_by_dependency=False):
a6f80f0e 264 """ Runs interface list through sub operation handler. """
265
be0b20f2 266 ifupdownobj.logger.debug('running operation %s on all given interfaces'
267 %op)
a6f80f0e 268 iface_run_queue = deque(ifacenames)
269 for i in range(0, len(iface_run_queue)):
d08d5f54 270 if op.endswith('up'):
a6f80f0e 271 # XXX: simplify this
d08d5f54 272 if sorted_by_dependency:
a6f80f0e 273 ifacename = iface_run_queue.pop()
274 else:
275 ifacename = iface_run_queue.popleft()
276 else:
d08d5f54 277 if sorted_by_dependency:
a6f80f0e 278 ifacename = iface_run_queue.popleft()
279 else:
280 ifacename = iface_run_queue.pop()
281
282 try:
31a5f4c3 283 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
a6f80f0e 284 for ifaceobj in ifaceobjs:
a6f80f0e 285 cenv = ifupdownobj.generate_running_env(ifaceobj, op)
be0b20f2 286 cls.run_iface_op(ifupdownobj, ifaceobj, op, cenv)
a6f80f0e 287 except Exception, e:
be0b20f2 288 ifupdownobj.log_error(str(e))
a6f80f0e 289
be0b20f2 290 @classmethod
291 def run_iface_list_ops(cls, ifupdownobj, ifacenames, ops,
292 sorted_by_dependency=False):
a6f80f0e 293 """ Runs interface list through sub operations handler
294
295 Unlike run_iface_list, this method executes a sub operation on the
296 entire interface list before proceeding to the next sub-operation.
297 ie operation 'pre-up' is run through the entire interface list before
298 'up'
299 """
a6f80f0e 300 # Each sub operation has a module list
be0b20f2 301 [cls.run_iface_list_op(ifupdownobj, ifacenames, op,
d08d5f54 302 sorted_by_dependency) for op in ops]
a6f80f0e 303
be0b20f2 304 @classmethod
305 def run_iface_dependency_graphs_sorted(cls, ifupdownobj,
306 dependency_graphs,
307 ops, indegrees=None,
308 graphsortall=False):
d08d5f54 309 """ runs interface dependency graph by topologically sorting the interfaces """
a6f80f0e 310
37c0543d 311 if indegrees is None:
312 indegrees = OrderedDict()
313 for ifacename in dependency_graphs.keys():
314 indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
a6f80f0e 315
be0b20f2 316 ifupdownobj.logger.debug('indegree array :')
317 ifupdownobj.logger.debug(ifupdownobj.pp.pformat(indegrees))
a6f80f0e 318
319 try:
be0b20f2 320 ifupdownobj.logger.debug('calling topological sort on the graph ' +
321 '...')
d08d5f54 322 if graphsortall:
37c0543d 323 sorted_ifacenames = graph.topological_sort_graphs_all(
324 dependency_graphs, indegrees)
325 else:
326 sorted_ifacenames = graph.topological_sort_graphs(
327 dependency_graphs, indegrees)
cca03c30 328 except Exception:
a6f80f0e 329 raise
330
be0b20f2 331 ifupdownobj.logger.debug('sorted iface list = %s' %sorted_ifacenames)
332 cls.run_iface_list_ops(ifupdownobj, sorted_ifacenames, ops,
333 sorted_by_dependency=True)
a6f80f0e 334
335
d08d5f54 336 """ Methods to execute interfaces in parallel """
be0b20f2 337 @classmethod
338 def init_tokens(cls, count):
339 cls.token_pool = BoundedSemaphore(count)
a6f80f0e 340
be0b20f2 341 @classmethod
342 def accquire_token(cls, logprefix=''):
343 cls.token_pool.acquire()
a6f80f0e 344
be0b20f2 345 @classmethod
346 def release_token(cls, logprefix=''):
347 cls.token_pool.release()
a6f80f0e 348
be0b20f2 349 @classmethod
350 def run_iface_parallel(cls, ifupdownobj, ifacename, op):
a6f80f0e 351 """ Configures interface in parallel.
352
353 Executes all its direct dependents in parallel
354
355 """
356
be0b20f2 357 ifupdownobj.logger.debug('%s:' %ifacename + ' %s' %op)
358 cls.accquire_token(iface)
a6f80f0e 359
360 # Each iface can have a list of objects
31a5f4c3 361 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
a6f80f0e 362 if ifaceobjs is None:
be0b20f2 363 ifupdownobj.logger.warning('%s: ' %ifacename + 'not found')
364 cls.release_token(ifacename)
a6f80f0e 365 return -1
366
367 for ifaceobj in ifaceobjs:
368 # Run dependents
f3215127 369 dlist = ifaceobj.get_lowerifaces()
be0b20f2 370 if dlist:
371 ifupdownobj.logger.debug('%s:' %ifacename +
a6f80f0e 372 ' found dependents: %s' %str(dlist))
373 try:
be0b20f2 374 cls.release_token(ifacename)
375 cls.run_iface_list_parallel(ifacename, ifupdownobj,
a6f80f0e 376 dlist, op)
be0b20f2 377 cls.accquire_token(ifacename)
a6f80f0e 378 except Exception, e:
be0b20f2 379 if ifupdownobj.ignore_error(str(e)):
a6f80f0e 380 pass
381 else:
382 # Dont bring the iface up if children did not come up
be0b20f2 383 ifupdownobj.logger.debug('%s:' %ifacename +
a6f80f0e 384 ' there was an error bringing %s' %op +
385 ' dependents (%s)', str(e))
386 ifupdownobj.set_iface_state(ifaceobj,
d08d5f54 387 ifaceState.from_str(ops[0]),
a6f80f0e 388 ifaceStatus.ERROR)
389 return -1
390
a6f80f0e 391 # Run all sub operations sequentially
392 try:
be0b20f2 393 ifupdownobj.logger.debug('%s:' %ifacename +
394 ' running sub-operations')
395 cls.run_iface_ops(ifupdownobj, ifaceobj, op)
a6f80f0e 396 except Exception, e:
be0b20f2 397 ifupdownobj.logger.error('%s:' %ifacename +
a6f80f0e 398 ' error running sub operations (%s)' %str(e))
399
be0b20f2 400 cls.release_token(ifacename)
a6f80f0e 401
be0b20f2 402 @classmethod
403 def run_iface_list_parallel(cls, parent, ifupdownobj, ifacenames, op):
a6f80f0e 404 """ Runs interface list in parallel """
405
406 running_threads = OrderedDict()
407 err = 0
408
409 for ifacename in ifacenames:
410 try:
be0b20f2 411 cls.accquire_token(parent)
a6f80f0e 412 running_threads[ifacename] = Thread(None,
be0b20f2 413 cls.run_iface_parallel, ifacename,
a6f80f0e 414 args=(ifupdownobj, ifacename, op))
415 running_threads[ifacename].start()
be0b20f2 416 cls.release_token(parent)
a6f80f0e 417 except Exception, e:
be0b20f2 418 cls.release_token(parent)
d08d5f54 419 if ifupdownobj.ignore_error(str(e)):
a6f80f0e 420 pass
421 else:
422 raise Exception('error starting thread for iface %s'
423 %ifacename)
424
425
be0b20f2 426 ifupdownobj.logger.debug('%s ' %parent +
427 'waiting for all the threads ...')
a6f80f0e 428 for ifacename, t in running_threads.items():
429 t.join()
430 if ifupdownobj.get_iface_status(ifacename) != ifaceStatus.SUCCESS:
431 err += 1
432
433 return err
434
be0b20f2 435 @classmethod
436 def run_iface_graphs_parallel(cls, parent, ifupdownobj, ifacenames, op):
a6f80f0e 437 """ Runs iface graphs in parallel """
438
439 running_threads = OrderedDict()
440 err = 0
441
442 for ifacename in ifacenames:
443 try:
be0b20f2 444 cls.accquire_graph_token(parent)
a6f80f0e 445 running_threads[ifacename] = Thread(None,
be0b20f2 446 cls.run_iface_parallel, ifacename,
a6f80f0e 447 args=(ifupdownobj, ifacename, op))
448 running_threads[ifacename].start()
be0b20f2 449 cls.release_graph_token(parent)
a6f80f0e 450 except Exception, e:
be0b20f2 451 cls.release_graph_token(parent)
d08d5f54 452 if ifupdownobj.ignore_error(str(e)):
a6f80f0e 453 pass
454 else:
455 raise Exception('error starting thread for iface %s'
456 %ifacename)
457
be0b20f2 458 ifupdownobj.logger.info('%s ' %parent +
459 'waiting for all the threads ...')
460 for ifacename, t in running_threads.items():
a6f80f0e 461 t.join()
462 # Check status of thread
463 # XXX: Check all objs
464 if ifupdownobj.get_iface_status(ifacename) != ifaceStatus.SUCCESS:
465 err += 1
a6f80f0e 466 return err
467
be0b20f2 468 @classmethod
469 def run_iface_dependency_graph_parallel(cls, ifupdownobj, dependency_graph,
a6f80f0e 470 operation):
471 """ Runs iface dependeny graph in parallel.
472
473 arguments:
474 ifupdownobj -- ifupdown object (used for getting and updating iface
475 object state)
476 dependency_graph -- dependency graph with
477 operation -- 'up' or 'down' or 'query'
478
479 """
480
be0b20f2 481 ifupdownobj.logger.debug('running dependency graph in parallel ..')
a6f80f0e 482 run_queue = []
a6f80f0e 483 # Build a list of ifaces that dont have any dependencies
484 for ifacename in dependency_graph.keys():
485 if ifupdownobj.get_iface_refcnt(ifacename) == 0:
486 run_queue.append(ifacename)
487
be0b20f2 488 ifupdownobj.logger.debug('graph roots (interfaces that dont'
d08d5f54 489 ' have dependents):' + ' %s' %str(run_queue))
be0b20f2 490 cls.init_tokens(ifupdownobj.get_njobs())
491 return cls.run_iface_list_parallel('main', ifupdownobj, run_queue,
a6f80f0e 492 operation)
493
494 # OR
495 # Run one graph at a time
496 #for iface in run_queue:
497 # self.run_iface_list_parallel('main', ifupdownobj, [iface],
498 # operation)
499