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