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