]> git.proxmox.com Git - mirror_ifupdown2.git/blame - pkg/scheduler.py
Fix use of args.CLASS with ifreload (reload does not support CLASS yet).
[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():
c798b0f4 24 INORDER = 0x1
25 POSTORDER = 0x2
d08d5f54 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.
99b212b0 32
33 Algo:
34 - run topological sort on the iface objects
35 - In the sorted iface object list, pick up interfaces with no parents
36 and run ops on them and their children.
37 - If operation is up and user gave interface list (ie not all)
38 option, also see if there were upper-devices and run ops on them.
39 - if operation is down, dont down the interface if it still
40 has upperifaces present. The down operation is executed when the
41 last upperiface goes away. If force option is set, this rule does not
42 apply.
43 - run ops calls addon modules run operation passing the iface
44 object and op to each module.
45 - ops are [pre-up, up, post-up, pre-down, down,
46 post-down, query-running, query-check]
a6f80f0e 47 """
48
c798b0f4 49 _STATE_CHECK = True
50
5973036b
RP
51 _SCHED_RETVAL = True
52
be0b20f2 53 @classmethod
44a6ca06 54 def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv=None):
a6f80f0e 55 """ Runs sub operation on an interface """
62ddec8b 56 ifacename = ifaceobj.name
a6f80f0e 57
c798b0f4 58 if (cls._STATE_CHECK and
5973036b 59 (ifaceobj.state >= ifaceState.from_str(op))):
be0b20f2 60 ifupdownobj.logger.debug('%s: already in state %s' %(ifacename, op))
d08d5f54 61 return
20dd6242 62 if not ifupdownobj.ADDONS_ENABLE: return
44a6ca06
RP
63 if op == 'query-checkcurr':
64 query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj)
be0b20f2 65 for mname in ifupdownobj.module_ops.get(op):
37c0543d 66 m = ifupdownobj.modules.get(mname)
a6f80f0e 67 err = 0
68 try:
d08d5f54 69 if hasattr(m, 'run'):
a690dfae 70 msg = ('%s: %s : running module %s' %(ifacename, op, mname))
739f665b 71 if op == 'query-checkcurr':
d08d5f54 72 # Dont check curr if the interface object was
37c0543d 73 # auto generated
d08d5f54 74 if (ifaceobj.priv_flags & ifupdownobj.NOCONFIG):
37c0543d 75 continue
a690dfae 76 ifupdownobj.logger.debug(msg)
44a6ca06 77 m.run(ifaceobj, op, query_ifaceobj)
a6f80f0e 78 else:
a690dfae 79 ifupdownobj.logger.debug(msg)
d08d5f54 80 m.run(ifaceobj, op)
a6f80f0e 81 except Exception, e:
82 err = 1
be0b20f2 83 ifupdownobj.log_error(str(e))
699c1cff
RP
84 err = 0 # error can be ignored by log_error, in which case
85 # reset err flag
a6f80f0e 86 finally:
31a5f4c3 87 if err:
88 ifaceobj.set_state_n_status(ifaceState.from_str(op),
89 ifaceStatus.ERROR)
5973036b
RP
90 if 'up' in op or 'down' in op:
91 cls._SCHED_RETVAL = False
d08d5f54 92 else:
31a5f4c3 93 ifaceobj.set_state_n_status(ifaceState.from_str(op),
94 ifaceStatus.SUCCESS)
6bd7fc74 95
96 if ifupdownobj.COMPAT_EXEC_SCRIPTS:
97 # execute /etc/network/ scripts
98 for mname in ifupdownobj.script_ops.get(op, []):
99 ifupdownobj.logger.debug('%s: %s : running script %s'
d08d5f54 100 %(ifacename, op, mname))
6bd7fc74 101 try:
102 ifupdownobj.exec_command(mname, cmdenv=cenv)
103 except Exception, e:
104 ifupdownobj.log_error(str(e))
37c0543d 105
be0b20f2 106 @classmethod
923290bd 107 def run_iface_list_ops(cls, ifupdownobj, ifaceobjs, ops):
108 """ Runs all operations on a list of interface
109 configurations for the same interface
110 """
a690dfae 111 # minor optimization. If operation is 'down', proceed only
112 # if interface exists in the system
923290bd 113 ifacename = ifaceobjs[0].name
114 if ('down' in ops[0] and
115 not ifupdownobj.link_exists(ifacename)):
525f0a30 116 ifupdownobj.logger.debug('%s: does not exist' %ifacename)
923290bd 117 # run posthook before you get out of here, so that
118 # appropriate cleanup is done
119 posthookfunc = ifupdownobj.sched_hooks.get('posthook')
120 if posthookfunc:
121 for ifaceobj in ifaceobjs:
122 ifaceobj.status = ifaceStatus.SUCCESS
123 posthookfunc(ifupdownobj, ifaceobj, 'down')
a690dfae 124 return
923290bd 125 for op in ops:
126 # first run ifupdownobj handlers. This is good enough
127 # for the first object in the list
128 handler = ifupdownobj.ops_handlers.get(op)
129 if handler:
130 if (not ifaceobjs[0].addr_method or
131 (ifaceobjs[0].addr_method and
132 ifaceobjs[0].addr_method != 'manual')):
133 handler(ifupdownobj, ifaceobjs[0])
134 for ifaceobj in ifaceobjs:
135 cls.run_iface_op(ifupdownobj, ifaceobj, op,
923290bd 136 cenv=ifupdownobj.generate_running_env(ifaceobj, op)
137 if ifupdownobj.COMPAT_EXEC_SCRIPTS else None)
138 posthookfunc = ifupdownobj.sched_hooks.get('posthook')
139 if posthookfunc:
140 posthookfunc(ifupdownobj, ifaceobj, op)
21c7daa7 141
142 @classmethod
fa3da4be 143 def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
144 followdependents=False):
99b212b0 145 """ Check if upperifaces are hanging off us and help caller decide
146 if he can proceed with the ops on this device
21c7daa7 147
99b212b0 148 Returns True or False indicating the caller to proceed with the
149 operation.
150 """
86fc62e2 151 # proceed only for down operation
152 if 'down' not in ops[0]:
153 return True
154
65c48517 155 if (ifupdownobj.FORCE or
156 not ifupdownobj.ADDONS_ENABLE or
86fc62e2 157 (not ifupdownobj.is_ifaceobj_noconfig(ifaceobj) and
33e106da
RP
158 ifupdownobj.config.get('warn_on_ifdown', '0') == '0' and
159 not ifupdownobj.ALL)):
99b212b0 160 return True
65c48517 161
62ddec8b 162 ulist = ifaceobj.upperifaces
99b212b0 163 if not ulist:
164 return True
33e106da 165
99b212b0 166 # Get the list of upper ifaces other than the parent
167 tmpulist = ([u for u in ulist if u != parent] if parent
168 else ulist)
169 if not tmpulist:
170 return True
171 # XXX: This is expensive. Find a cheaper way to do this.
172 # if any of the upperdevs are present,
173 # return false to the caller to skip this interface
174 for u in tmpulist:
175 if ifupdownobj.link_exists(u):
176 if not ifupdownobj.ALL:
86fc62e2 177 if ifupdownobj.is_ifaceobj_noconfig(ifaceobj):
178 ifupdownobj.logger.info('%s: skipping interface down,'
179 %ifaceobj.name + ' upperiface %s still around ' %u)
180 else:
181 ifupdownobj.logger.warn('%s: skipping interface down,'
182 %ifaceobj.name + ' upperiface %s still around ' %u)
99b212b0 183 return False
21c7daa7 184 return True
185
be0b20f2 186 @classmethod
187 def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
d08d5f54 188 order=ifaceSchedulerFlags.POSTORDER,
189 followdependents=True):
6ef5bfa2 190 """ runs interface by traversing all nodes rooted at itself """
191
d08d5f54 192 # Each ifacename can have a list of iface objects
31a5f4c3 193 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
194 if not ifaceobjs:
d08d5f54 195 raise Exception('%s: not found' %ifacename)
a6f80f0e 196
d08d5f54 197 for ifaceobj in ifaceobjs:
ca3f4fc7 198 if not cls._check_upperifaces(ifupdownobj, ifaceobj,
199 ops, parent, followdependents):
21c7daa7 200 return
f3215127 201
923290bd 202 # If inorder, run the iface first and then its dependents
203 if order == ifaceSchedulerFlags.INORDER:
204 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
205
206 for ifaceobj in ifaceobjs:
f3215127 207 # Run lowerifaces or dependents
62ddec8b 208 dlist = ifaceobj.lowerifaces
f3215127 209 if dlist:
fa3da4be 210 ifupdownobj.logger.debug('%s: found dependents %s'
211 %(ifacename, str(dlist)))
d08d5f54 212 try:
213 if not followdependents:
214 # XXX: this is yet another extra step,
215 # but is needed for interfaces that are
f3215127 216 # implicit dependents. even though we are asked to
217 # not follow dependents, we must follow the ones
218 # that dont have user given config. Because we own them
d08d5f54 219 new_dlist = [d for d in dlist
923290bd 220 if ifupdownobj.is_iface_noconfig(d)]
6ef5bfa2 221 if new_dlist:
be0b20f2 222 cls.run_iface_list(ifupdownobj, new_dlist, ops,
923290bd 223 ifacename, order, followdependents,
224 continueonfailure=False)
d08d5f54 225 else:
be0b20f2 226 cls.run_iface_list(ifupdownobj, dlist, ops,
f3215127 227 ifacename, order,
228 followdependents,
6ef5bfa2 229 continueonfailure=False)
d08d5f54 230 except Exception, e:
be0b20f2 231 if (ifupdownobj.ignore_error(str(e))):
d08d5f54 232 pass
233 else:
234 # Dont bring the iface up if children did not come up
a690dfae 235 ifaceobj.set_state_n_status(ifaceState.NEW,
923290bd 236 ifaceStatus.ERROR)
d08d5f54 237 raise
923290bd 238 if order == ifaceSchedulerFlags.POSTORDER:
239 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
a6f80f0e 240
be0b20f2 241 @classmethod
242 def run_iface_list(cls, ifupdownobj, ifacenames,
f3215127 243 ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
6ef5bfa2 244 followdependents=True, continueonfailure=True):
d08d5f54 245 """ Runs interface list """
a6f80f0e 246
d08d5f54 247 for ifacename in ifacenames:
a6f80f0e 248 try:
be0b20f2 249 cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
d08d5f54 250 order, followdependents)
a6f80f0e 251 except Exception, e:
6ef5bfa2 252 if continueonfailure:
69f58278 253 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
254 traceback.print_tb(sys.exc_info()[2])
be0b20f2 255 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
d08d5f54 256 pass
257 else:
be0b20f2 258 if (ifupdownobj.ignore_error(str(e))):
6ef5bfa2 259 pass
260 else:
9dce3561 261 raise Exception('%s : (%s)' %(ifacename, str(e)))
d08d5f54 262
be0b20f2 263 @classmethod
c798b0f4 264 def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
265 followdependents=True, skip_root=False):
266 """ runs interface by traversing all nodes rooted at itself """
267
268 # Each ifacename can have a list of iface objects
269 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
270 if not ifaceobjs:
271 raise Exception('%s: not found' %ifacename)
272
923290bd 273 if not skip_root:
274 # run the iface first and then its upperifaces
275 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
c798b0f4 276 for ifaceobj in ifaceobjs:
c798b0f4 277 # Run upperifaces
62ddec8b 278 ulist = ifaceobj.upperifaces
c798b0f4 279 if ulist:
fa3da4be 280 ifupdownobj.logger.debug('%s: found upperifaces %s'
281 %(ifacename, str(ulist)))
c798b0f4 282 try:
283 cls.run_iface_list_upper(ifupdownobj, ulist, ops,
284 ifacename,
285 followdependents,
286 continueonfailure=True)
287 except Exception, e:
288 if (ifupdownobj.ignore_error(str(e))):
289 pass
290 else:
291 raise
292
293 @classmethod
294 def run_iface_list_upper(cls, ifupdownobj, ifacenames,
295 ops, parent=None, followdependents=True,
296 continueonfailure=True, skip_root=False):
297 """ Runs interface list """
298
299 for ifacename in ifacenames:
300 try:
301 cls.run_iface_graph_upper(ifupdownobj, ifacename, ops, parent,
302 followdependents, skip_root)
303 except Exception, e:
304 if continueonfailure:
305 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
306 traceback.print_tb(sys.exc_info()[2])
307 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
308 pass
309 else:
310 if (ifupdownobj.ignore_error(str(e))):
311 pass
312 else:
9dce3561 313 raise Exception('%s : (%s)' %(ifacename, str(e)))
c798b0f4 314
5ee3e1a8
RP
315 @classmethod
316 def get_sorted_iface_list(cls, ifupdownobj, ifacenames, ops,
317 dependency_graph, indegrees=None):
318 if len(ifacenames) == 1:
319 return ifacenames
320 # Get a sorted list of all interfaces
321 if not indegrees:
322 indegrees = OrderedDict()
323 for ifacename in dependency_graph.keys():
324 indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
325 ifacenames_all_sorted = graph.topological_sort_graphs_all(
326 dependency_graph, indegrees)
327 # if ALL was set, return all interfaces
328 if ifupdownobj.ALL:
329 return ifacenames_all_sorted
330
331 # else return ifacenames passed as argument in sorted order
332 ifacenames_sorted = []
333 [ifacenames_sorted.append(ifacename)
334 for ifacename in ifacenames_all_sorted
335 if ifacename in ifacenames]
336 return ifacenames_sorted
337
c798b0f4 338 @classmethod
339 def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
340 dependency_graph=None, indegrees=None,
d08d5f54 341 order=ifaceSchedulerFlags.POSTORDER,
342 followdependents=True):
343 """ Runs iface dependeny graph by visiting all the nodes
344
345 Parameters:
346 -----------
347 ifupdownobj : ifupdown object (used for getting and updating iface
348 object state)
349 dependency_graph : dependency graph in adjacency list
350 format (contains more than one dependency graph)
351 ops : list of operations to perform eg ['pre-up', 'up', 'post-up']
352
5ee3e1a8
RP
353 indegrees : indegree array if present is used to topologically sort
354 the graphs in the dependency_graph
d08d5f54 355 """
5ee3e1a8
RP
356 #
357 # Algo:
358 # if ALL/auto interfaces are specified,
359 # - walk the dependency tree in postorder or inorder depending
360 # on the operation.
361 # (This is to run interfaces correctly in order)
362 # else:
363 # - sort iface list if the ifaces belong to a "class"
364 # - else just run iface list in the order they were specified
365 #
366 # Run any upperifaces if available
367 #
368 followupperifaces = []
369 run_queue = []
370 skip_ifacesort = int(ifupdownobj.config.get('skip_ifacesort', '0'))
371 if not skip_ifacesort and not indegrees:
372 indegrees = OrderedDict()
373 for ifacename in dependency_graph.keys():
374 indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
c798b0f4 375
5ee3e1a8 376 if not ifupdownobj.ALL:
ca3f4fc7 377 # If there is any interface that does exist, maybe it is a
5ee3e1a8
RP
378 # logical interface and we have to followupperifaces when it
379 # comes up, so get that list.
ca3f4fc7 380 followupperifaces = (True if
381 [i for i in ifacenames
923290bd 382 if not ifupdownobj.link_exists(i)]
383 else False)
5ee3e1a8
RP
384 if not skip_ifacesort and ifupdownobj.IFACE_CLASS:
385 # sort interfaces only if allow class was specified and
386 # not skip_ifacesort
387 run_queue = cls.get_sorted_iface_list(ifupdownobj, ifacenames,
388 ops, dependency_graph, indegrees)
389 if run_queue and 'up' in ops[0]:
390 run_queue.reverse()
ba7b1d60 391 else:
5ee3e1a8
RP
392 # if -a is set, we dont really have to sort. We pick the interfaces
393 # that have no parents and
394 if not skip_ifacesort:
395 sorted_ifacenames = cls.get_sorted_iface_list(ifupdownobj,
396 ifacenames, ops, dependency_graph,
397 indegrees)
398 if sorted_ifacenames:
399 # pick interfaces that user asked
400 # and those that dont have any dependents first
401 [run_queue.append(ifacename)
402 for ifacename in sorted_ifacenames
403 if ifacename in ifacenames and
404 not indegrees.get(ifacename)]
405 ifupdownobj.logger.debug('graph roots (interfaces that ' +
406 'dont have dependents):' + ' %s' %str(run_queue))
407 else:
408 ifupdownobj.logger.warn('interface sort returned None')
409
410 # If queue not present, just run interfaces that were asked by the user
411 if not run_queue:
412 run_queue = list(ifacenames)
413 if 'down' in ops[0]:
414 run_queue.reverse()
415
416 # run interface list
417 ifupdownobj.logger.info('running interfaces: %s' %str(run_queue))
418 cls.run_iface_list(ifupdownobj, run_queue, ops,
419 parent=None, order=order,
420 followdependents=followdependents)
421 if (((not ifupdownobj.ALL and followdependents) or
422 followupperifaces) and
423 'up' in ops[0]):
424 # If user had given a set of interfaces to bring up
425 # try and execute 'up' on the upperifaces
426 ifupdownobj.logger.info('running upperifaces if available ..')
427 cls._STATE_CHECK = False
428 cls.run_iface_list_upper(ifupdownobj, ifacenames, ops,
429 skip_root=True)
430 cls._STATE_CHECK = True
5973036b
RP
431
432 if not cls._SCHED_RETVAL:
433 raise Exception()