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