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