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