]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown/scheduler.py
Fix missing import due to merge
[mirror_ifupdown2.git] / ifupdown / scheduler.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6 # ifaceScheduler --
7 # interface scheduler
8 #
9
10 from statemanager import *
11 from iface import *
12 from graph import *
13 from collections import deque
14 from collections import OrderedDict
15 import logging
16 import traceback
17 import sys
18 from graph import *
19 from collections import deque
20 from threading import *
21 from ifupdownbase import *
22
23 class ifaceSchedulerFlags():
24 """ Enumerates scheduler flags """
25
26 INORDER = 0x1
27 POSTORDER = 0x2
28
29 class ifaceScheduler():
30 """ scheduler functions to schedule configuration of interfaces.
31
32 supports scheduling of interfaces serially in plain interface list
33 or dependency graph format.
34
35 """
36
37 _STATE_CHECK = True
38
39 _SCHED_RETVAL = True
40
41 @classmethod
42 def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv=None):
43 """ Runs sub operation on an interface """
44 ifacename = ifaceobj.name
45
46 if (cls._STATE_CHECK and
47 (ifaceobj.state >= ifaceState.from_str(op))):
48 ifupdownobj.logger.debug('%s: already in state %s' %(ifacename, op))
49 return
50 if not ifupdownobj.ADDONS_ENABLE: return
51 if op == 'query-checkcurr':
52 query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj)
53 for mname in ifupdownobj.module_ops.get(op):
54 m = ifupdownobj.modules.get(mname)
55 err = 0
56 try:
57 if hasattr(m, 'run'):
58 msg = ('%s: %s : running module %s' %(ifacename, op, mname))
59 if op == 'query-checkcurr':
60 # Dont check curr if the interface object was
61 # auto generated
62 if (ifaceobj.priv_flags & ifupdownobj.NOCONFIG):
63 continue
64 ifupdownobj.logger.debug(msg)
65 m.run(ifaceobj, op, query_ifaceobj)
66 else:
67 ifupdownobj.logger.debug(msg)
68 m.run(ifaceobj, op)
69 except Exception, e:
70 err = 1
71 ifupdownobj.log_error(str(e))
72 err = 0 # error can be ignored by log_error, in which case
73 # reset err flag
74 finally:
75 if err or ifaceobj.status == ifaceStatus.ERROR:
76 ifaceobj.set_state_n_status(ifaceState.from_str(op),
77 ifaceStatus.ERROR)
78 if 'up' in op or 'down' in op:
79 cls._SCHED_RETVAL = False
80 else:
81 ifaceobj.set_state_n_status(ifaceState.from_str(op),
82 ifaceStatus.SUCCESS)
83
84 if ifupdownobj.config.get('addon_scripts_support', '0') == '1':
85 # execute /etc/network/ scripts
86 for mname in ifupdownobj.script_ops.get(op, []):
87 ifupdownobj.logger.debug('%s: %s : running script %s'
88 %(ifacename, op, mname))
89 try:
90 ifupdownobj.exec_command(mname, cmdenv=cenv)
91 except Exception, e:
92 ifupdownobj.log_error(str(e))
93
94 @classmethod
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 """
99 # minor optimization. If operation is 'down', proceed only
100 # if interface exists in the system
101 ifacename = ifaceobjs[0].name
102 ifupdownobj.logger.info('%s: running ops ...' %ifacename)
103 if ('down' in ops[0] and ifaceobjs[0].type != ifaceType.BRIDGE_VLAN and
104 not ifupdownobj.link_exists(ifacename)):
105 ifupdownobj.logger.debug('%s: does not exist' %ifacename)
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')
113 return
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,
125 cenv=ifupdownobj.generate_running_env(ifaceobj, op)
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]
132
133 @classmethod
134 def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
135 followdependents=False):
136 """ Check if upperifaces are hanging off us and help caller decide
137 if he can proceed with the ops on this device
138
139 Returns True or False indicating the caller to proceed with the
140 operation.
141 """
142 # proceed only for down operation
143 if 'down' not in ops[0]:
144 return True
145
146 if (ifupdownobj.FORCE or
147 not ifupdownobj.ADDONS_ENABLE or
148 (not ifupdownobj.is_ifaceobj_noconfig(ifaceobj) and
149 ifupdownobj.config.get('warn_on_ifdown', '0') == '0' and
150 not ifupdownobj.ALL)):
151 return True
152
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
174 ulist = ifaceobj.upperifaces
175 if not ulist:
176 return True
177
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:
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)
195 return False
196 return True
197
198 @classmethod
199 def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
200 order=ifaceSchedulerFlags.POSTORDER,
201 followdependents=True):
202 """ runs interface by traversing all nodes rooted at itself """
203
204 # Each ifacename can have a list of iface objects
205 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
206 if not ifaceobjs:
207 raise Exception('%s: not found' %ifacename)
208
209 for ifaceobj in ifaceobjs:
210 if not cls._check_upperifaces(ifupdownobj, ifaceobj,
211 ops, parent, followdependents):
212 return
213
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:
219 # Run lowerifaces or dependents
220 dlist = ifaceobj.lowerifaces
221 if dlist:
222 ifupdownobj.logger.debug('%s: found dependents %s'
223 %(ifacename, str(dlist)))
224 try:
225 if not followdependents:
226 # XXX: this is yet another extra step,
227 # but is needed for interfaces that are
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
231 new_dlist = [d for d in dlist
232 if ifupdownobj.is_iface_noconfig(d)]
233 if new_dlist:
234 cls.run_iface_list(ifupdownobj, new_dlist, ops,
235 ifacename, order, followdependents,
236 continueonfailure=False)
237 else:
238 cls.run_iface_list(ifupdownobj, dlist, ops,
239 ifacename, order,
240 followdependents,
241 continueonfailure=False)
242 except Exception, e:
243 if (ifupdownobj.ignore_error(str(e))):
244 pass
245 else:
246 # Dont bring the iface up if children did not come up
247 ifaceobj.set_state_n_status(ifaceState.NEW,
248 ifaceStatus.ERROR)
249 raise
250 if order == ifaceSchedulerFlags.POSTORDER:
251 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
252
253 @classmethod
254 def run_iface_list(cls, ifupdownobj, ifacenames,
255 ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
256 followdependents=True, continueonfailure=True):
257 """ Runs interface list """
258
259 for ifacename in ifacenames:
260 try:
261 cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
262 order, followdependents)
263 except Exception, e:
264 if continueonfailure:
265 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
266 traceback.print_tb(sys.exc_info()[2])
267 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
268 pass
269 else:
270 if (ifupdownobj.ignore_error(str(e))):
271 pass
272 else:
273 raise Exception('%s : (%s)' %(ifacename, str(e)))
274
275 @classmethod
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
285 if not skip_root:
286 # run the iface first and then its upperifaces
287 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
288 for ifaceobj in ifaceobjs:
289 # Run upperifaces
290 ulist = ifaceobj.upperifaces
291 if ulist:
292 ifupdownobj.logger.debug('%s: found upperifaces %s'
293 %(ifacename, str(ulist)))
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:
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
320
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
344 @classmethod
345 def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
346 dependency_graph=None, indegrees=None,
347 order=ifaceSchedulerFlags.POSTORDER,
348 followdependents=True):
349 """ runs interface configuration modules on interfaces passed as
350 argument. Runs topological sort on interface dependency graph.
351
352 Args:
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
360
361 Kwargs:
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
367
368 """
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 #
381 followupperifaces = False
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)
388
389 if not ifupdownobj.ALL:
390 # If there is any interface that does exist, maybe it is a
391 # logical interface and we have to followupperifaces when it
392 # comes up, so get that list.
393 if any([True for i in ifacenames
394 if ifupdownobj.must_follow_upperifaces(i)]):
395 followupperifaces = (True if
396 [i for i in ifacenames
397 if not ifupdownobj.link_exists(i)]
398 else False)
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()
406 else:
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
432 cls.run_iface_list(ifupdownobj, run_queue, ops,
433 parent=None, order=order,
434 followdependents=followdependents)
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
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
444 ifupdownobj.logger.info('running upperifaces (parent interfaces) ' +
445 'if available ..')
446 cls._STATE_CHECK = False
447 cls.run_iface_list_upper(ifupdownobj, ifacenames, ops,
448 skip_root=True)
449 cls._STATE_CHECK = True