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