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