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