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