]> git.proxmox.com Git - mirror_ifupdown2.git/blame_incremental - ifupdown/scheduler.py
Change "source" inclusion errors to warns.
[mirror_ifupdown2.git] / ifupdown / scheduler.py
... / ...
CommitLineData
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
10from statemanager import *
11from iface import *
12from graph import *
13from collections import deque
14from collections import OrderedDict
15import logging
16import traceback
17import sys
18from graph import *
19from collections import deque
20from threading import *
21from ifupdownbase import *
22
23class ifaceSchedulerFlags():
24 """ Enumerates scheduler flags """
25
26 INORDER = 0x1
27 POSTORDER = 0x2
28
29class 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 ifupdownobj.type and ifupdownobj.type != ifaceobj.Type:
47 return
48
49 if (cls._STATE_CHECK and
50 (ifaceobj.state >= ifaceState.from_str(op))):
51 ifupdownobj.logger.debug('%s: already in state %s' %(ifacename, op))
52 return
53 if not ifupdownobj.ADDONS_ENABLE: return
54 if op == 'query-checkcurr':
55 query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj)
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
63 for mname in ifupdownobj.module_ops.get(op):
64 m = ifupdownobj.modules.get(mname)
65 err = 0
66 try:
67 if hasattr(m, 'run'):
68 msg = ('%s: %s : running module %s' %(ifacename, op, mname))
69 if op == 'query-checkcurr':
70 # Dont check curr if the interface object was
71 # auto generated
72 if (ifaceobj.priv_flags & ifupdownobj.NOCONFIG):
73 continue
74 ifupdownobj.logger.debug(msg)
75 m.run(ifaceobj, op, query_ifaceobj,
76 ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
77 else:
78 ifupdownobj.logger.debug(msg)
79 m.run(ifaceobj, op, ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
80 except Exception, e:
81 err = 1
82 ifupdownobj.log_error(str(e))
83 err = 0 # error can be ignored by log_error, in which case
84 # reset err flag
85 finally:
86 if err or ifaceobj.status == ifaceStatus.ERROR:
87 ifaceobj.set_state_n_status(ifaceState.from_str(op),
88 ifaceStatus.ERROR)
89 if 'up' in op or 'down' in op:
90 cls._SCHED_RETVAL = False
91 else:
92 ifaceobj.set_state_n_status(ifaceState.from_str(op),
93 ifaceStatus.SUCCESS)
94
95 if ifupdownobj.config.get('addon_scripts_support', '0') == '1':
96 # execute /etc/network/ scripts
97 for mname in ifupdownobj.script_ops.get(op, []):
98 ifupdownobj.logger.debug('%s: %s : running script %s'
99 %(ifacename, op, mname))
100 try:
101 ifupdownobj.exec_command(mname, cmdenv=cenv)
102 except Exception, e:
103 ifupdownobj.log_error(str(e))
104
105 @classmethod
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 """
110 # minor optimization. If operation is 'down', proceed only
111 # if interface exists in the system
112 ifacename = ifaceobjs[0].name
113 ifupdownobj.logger.info('%s: running ops ...' %ifacename)
114 if ('down' in ops[0] and
115 ifaceobjs[0].type != ifaceType.BRIDGE_VLAN and
116 not ifupdownobj.link_exists(ifacename)):
117 ifupdownobj.logger.debug('%s: does not exist' %ifacename)
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')
125 return
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,
137 cenv=ifupdownobj.generate_running_env(ifaceobj, op)
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]
144
145 @classmethod
146 def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
147 followdependents=False):
148 """ Check if upperifaces are hanging off us and help caller decide
149 if he can proceed with the ops on this device
150
151 Returns True or False indicating the caller to proceed with the
152 operation.
153 """
154 # proceed only for down operation
155 if 'down' not in ops[0]:
156 return True
157
158 if (ifupdownobj.FORCE or
159 not ifupdownobj.ADDONS_ENABLE or
160 (not ifupdownobj.is_ifaceobj_noconfig(ifaceobj) and
161 ifupdownobj.config.get('warn_on_ifdown', '0') == '0' and
162 not ifupdownobj.ALL)):
163 return True
164
165 ulist = ifaceobj.upperifaces
166 if not ulist:
167 return True
168
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:
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)
186 return False
187 return True
188
189 @classmethod
190 def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
191 order=ifaceSchedulerFlags.POSTORDER,
192 followdependents=True):
193 """ runs interface by traversing all nodes rooted at itself """
194
195 # Each ifacename can have a list of iface objects
196 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
197 if not ifaceobjs:
198 raise Exception('%s: not found' %ifacename)
199
200 for ifaceobj in ifaceobjs:
201 if not cls._check_upperifaces(ifupdownobj, ifaceobj,
202 ops, parent, followdependents):
203 return
204
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:
210 # Run lowerifaces or dependents
211 dlist = ifaceobj.lowerifaces
212 if dlist:
213 ifupdownobj.logger.debug('%s: found dependents %s'
214 %(ifacename, str(dlist)))
215 try:
216 if not followdependents:
217 # XXX: this is yet another extra step,
218 # but is needed for interfaces that are
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
222 new_dlist = [d for d in dlist
223 if ifupdownobj.is_iface_noconfig(d)]
224 if new_dlist:
225 cls.run_iface_list(ifupdownobj, new_dlist, ops,
226 ifacename, order, followdependents,
227 continueonfailure=False)
228 else:
229 cls.run_iface_list(ifupdownobj, dlist, ops,
230 ifacename, order,
231 followdependents,
232 continueonfailure=False)
233 except Exception, e:
234 if (ifupdownobj.ignore_error(str(e))):
235 pass
236 else:
237 # Dont bring the iface up if children did not come up
238 ifaceobj.set_state_n_status(ifaceState.NEW,
239 ifaceStatus.ERROR)
240 raise
241 if order == ifaceSchedulerFlags.POSTORDER:
242 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
243
244 @classmethod
245 def run_iface_list(cls, ifupdownobj, ifacenames,
246 ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
247 followdependents=True, continueonfailure=True):
248 """ Runs interface list """
249
250 for ifacename in ifacenames:
251 try:
252 cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
253 order, followdependents)
254 except Exception, e:
255 if continueonfailure:
256 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
257 traceback.print_tb(sys.exc_info()[2])
258 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
259 pass
260 else:
261 if (ifupdownobj.ignore_error(str(e))):
262 pass
263 else:
264 raise Exception('%s : (%s)' %(ifacename, str(e)))
265
266 @classmethod
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
276 if not skip_root:
277 # run the iface first and then its upperifaces
278 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
279 for ifaceobj in ifaceobjs:
280 # Run upperifaces
281 ulist = ifaceobj.upperifaces
282 if ulist:
283 ifupdownobj.logger.debug('%s: found upperifaces %s'
284 %(ifacename, str(ulist)))
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:
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
311
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
335 @classmethod
336 def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
337 dependency_graph=None, indegrees=None,
338 order=ifaceSchedulerFlags.POSTORDER,
339 followdependents=True):
340 """ runs interface configuration modules on interfaces passed as
341 argument. Runs topological sort on interface dependency graph.
342
343 Args:
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
351
352 Kwargs:
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
358
359 """
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 #
372 followupperifaces = False
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)
379
380 if not ifupdownobj.ALL:
381 # If there is any interface that does exist, maybe it is a
382 # logical interface and we have to followupperifaces when it
383 # comes up, so get that list.
384 if any([True for i in ifacenames
385 if ifupdownobj.must_follow_upperifaces(i)]):
386 followupperifaces = (True if
387 [i for i in ifacenames
388 if not ifupdownobj.link_exists(i)]
389 else False)
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()
397 else:
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
423 cls.run_iface_list(ifupdownobj, run_queue, ops,
424 parent=None, order=order,
425 followdependents=followdependents)
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
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
435 ifupdownobj.logger.info('running upperifaces (parent interfaces) ' +
436 'if available ..')
437 cls._STATE_CHECK = False
438 cls.run_iface_list_upper(ifupdownobj, ifacenames, ops,
439 skip_root=True)
440 cls._STATE_CHECK = True