]> git.proxmox.com Git - mirror_ifupdown2.git/blob - pkg/scheduler.py
Remove upper device check warnings + implicitly follow upperifaces when
[mirror_ifupdown2.git] / pkg / scheduler.py
1 #!/usr/bin/python
2 #
3 # Copyright 2013. Cumulus Networks, Inc.
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 INORDER = 0x1
25 POSTORDER = 0x2
26
27 class ifaceScheduler():
28 """ scheduler functions to schedule configuration of interfaces.
29
30 supports scheduling of interfaces serially in plain interface list
31 or dependency graph format.
32 """
33
34 _STATE_CHECK = True
35
36 token_pool = None
37
38 @classmethod
39 def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv):
40 """ Runs sub operation on an interface """
41 ifacename = ifaceobj.name
42
43 if (cls._STATE_CHECK and
44 (ifaceobj.state >= ifaceState.from_str(op)) and
45 (ifaceobj.status == ifaceStatus.SUCCESS)):
46 ifupdownobj.logger.debug('%s: already in state %s' %(ifacename, op))
47 return
48
49 # first run ifupdownobj handlers
50 handler = ifupdownobj.ops_handlers.get(op)
51 if handler:
52 if not ifaceobj.addr_method or (ifaceobj.addr_method and
53 ifaceobj.addr_method != 'manual'):
54 handler(ifupdownobj, ifaceobj)
55
56 if not ifupdownobj.ADDONS_ENABLE: return
57
58 for mname in ifupdownobj.module_ops.get(op):
59 m = ifupdownobj.modules.get(mname)
60 err = 0
61 try:
62 if hasattr(m, 'run'):
63 msg = ('%s: %s : running module %s' %(ifacename, op, mname))
64 if op == 'query-checkcurr':
65 # Dont check curr if the interface object was
66 # auto generated
67 if (ifaceobj.priv_flags & ifupdownobj.NOCONFIG):
68 continue
69 ifupdownobj.logger.debug(msg)
70 m.run(ifaceobj, op,
71 query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj))
72 else:
73 ifupdownobj.logger.debug(msg)
74 m.run(ifaceobj, op)
75 except Exception, e:
76 err = 1
77 ifupdownobj.log_error(str(e))
78 finally:
79 if err:
80 ifaceobj.set_state_n_status(ifaceState.from_str(op),
81 ifaceStatus.ERROR)
82 else:
83 ifaceobj.set_state_n_status(ifaceState.from_str(op),
84 ifaceStatus.SUCCESS)
85
86 if ifupdownobj.COMPAT_EXEC_SCRIPTS:
87 # execute /etc/network/ scripts
88 for mname in ifupdownobj.script_ops.get(op, []):
89 ifupdownobj.logger.debug('%s: %s : running script %s'
90 %(ifacename, op, mname))
91 try:
92 ifupdownobj.exec_command(mname, cmdenv=cenv)
93 except Exception, e:
94 ifupdownobj.log_error(str(e))
95
96 @classmethod
97 def run_iface_ops(cls, ifupdownobj, ifaceobj, ops):
98 """ Runs all operations on an interface """
99 ifacename = ifaceobj.name
100 # minor optimization. If operation is 'down', proceed only
101 # if interface exists in the system
102 if 'down' in ops[0] and not ifupdownobj.link_exists(ifacename):
103 ifupdownobj.logger.info('%s: does not exist' %ifacename)
104 return
105 cenv=None
106 if ifupdownobj.COMPAT_EXEC_SCRIPTS:
107 # For backward compatibility generate env variables
108 # for attributes
109 cenv = ifupdownobj.generate_running_env(ifaceobj, ops[0])
110 map(lambda op: cls.run_iface_op(ifupdownobj, ifaceobj, op, cenv), ops)
111 posthookfunc = ifupdownobj.sched_hooks.get('posthook')
112 if posthookfunc:
113 posthookfunc(ifupdownobj, ifaceobj)
114
115
116 @classmethod
117 def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
118 followdependents=False):
119 """ Check if conflicting upper ifaces are around and warn if required
120
121 Returns False if this interface needs to be skipped,
122 else return True """
123
124 # XXX: simply return for now, the warnings this function prints
125 # are very confusing. Get rid of this function soon
126 return True
127
128 if 'up' in ops[0] and followdependents:
129 return True
130
131 # Deal with upperdevs first
132 ulist = ifaceobj.upperifaces
133 if ulist:
134 tmpulist = ([u for u in ulist if u != parent] if parent
135 else ulist)
136 if not tmpulist:
137 return True
138 if 'down' in ops[0]:
139 # XXX: This is expensive. Find a cheaper way to do this
140 # if any of the upperdevs are present,
141 # dont down this interface
142 for u in tmpulist:
143 if ifupdownobj.link_exists(u):
144 if not ifupdownobj.FORCE and not ifupdownobj.ALL:
145 ifupdownobj.logger.warn('%s: ' %ifaceobj.name +
146 'upperiface %s still around' %u)
147 return True
148 elif 'up' in ops[0] and not ifupdownobj.ALL:
149 # For 'up', just warn that there is an upperdev which is
150 # probably not up
151 for u in tmpulist:
152 if not ifupdownobj.link_exists(u):
153 ifupdownobj.logger.warn('%s: ' %ifaceobj.name +
154 'upper iface %s does not exist' %u)
155 return True
156
157 @classmethod
158 def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
159 order=ifaceSchedulerFlags.POSTORDER,
160 followdependents=True):
161 """ runs interface by traversing all nodes rooted at itself """
162
163 # Each ifacename can have a list of iface objects
164 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
165 if not ifaceobjs:
166 raise Exception('%s: not found' %ifacename)
167
168 for ifaceobj in ifaceobjs:
169 if not cls._check_upperifaces(ifupdownobj, ifaceobj,
170 ops, parent, followdependents):
171 return
172 if order == ifaceSchedulerFlags.INORDER:
173 # If inorder, run the iface first and then its dependents
174 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
175
176 # Run lowerifaces or dependents
177 dlist = ifaceobj.lowerifaces
178 if dlist:
179 ifupdownobj.logger.debug('%s: found dependents %s'
180 %(ifacename, str(dlist)))
181 try:
182 if not followdependents:
183 # XXX: this is yet another extra step,
184 # but is needed for interfaces that are
185 # implicit dependents. even though we are asked to
186 # not follow dependents, we must follow the ones
187 # that dont have user given config. Because we own them
188 new_dlist = [d for d in dlist
189 if ifupdownobj.is_iface_noconfig(d)]
190 if new_dlist:
191 cls.run_iface_list(ifupdownobj, new_dlist, ops,
192 ifacename, order,
193 followdependents,
194 continueonfailure=False)
195 else:
196 cls.run_iface_list(ifupdownobj, dlist, ops,
197 ifacename, order,
198 followdependents,
199 continueonfailure=False)
200 except Exception, e:
201 if (ifupdownobj.ignore_error(str(e))):
202 pass
203 else:
204 # Dont bring the iface up if children did not come up
205 ifaceobj.set_state_n_status(ifaceState.NEW,
206 ifaceStatus.ERROR)
207 raise
208 if order == ifaceSchedulerFlags.POSTORDER:
209 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
210
211 @classmethod
212 def run_iface_list(cls, ifupdownobj, ifacenames,
213 ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
214 followdependents=True, continueonfailure=True):
215 """ Runs interface list """
216
217 for ifacename in ifacenames:
218 try:
219 cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
220 order, followdependents)
221 except Exception, e:
222 if continueonfailure:
223 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
224 traceback.print_tb(sys.exc_info()[2])
225 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
226 pass
227 else:
228 if (ifupdownobj.ignore_error(str(e))):
229 pass
230 else:
231 raise Exception('error running iface %s (%s)'
232 %(ifacename, str(e)))
233
234 @classmethod
235 def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
236 followdependents=True, skip_root=False):
237 """ runs interface by traversing all nodes rooted at itself """
238
239 # Each ifacename can have a list of iface objects
240 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
241 if not ifaceobjs:
242 raise Exception('%s: not found' %ifacename)
243
244 for ifaceobj in ifaceobjs:
245 if not skip_root:
246 # run the iface first and then its upperifaces
247 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
248
249 # Run upperifaces
250 ulist = ifaceobj.upperifaces
251 if ulist:
252 ifupdownobj.logger.debug('%s: found upperifaces %s'
253 %(ifacename, str(ulist)))
254 try:
255 cls.run_iface_list_upper(ifupdownobj, ulist, ops,
256 ifacename,
257 followdependents,
258 continueonfailure=True)
259 except Exception, e:
260 if (ifupdownobj.ignore_error(str(e))):
261 pass
262 else:
263 raise
264
265 @classmethod
266 def run_iface_list_upper(cls, ifupdownobj, ifacenames,
267 ops, parent=None, followdependents=True,
268 continueonfailure=True, skip_root=False):
269 """ Runs interface list """
270
271 for ifacename in ifacenames:
272 try:
273 cls.run_iface_graph_upper(ifupdownobj, ifacename, ops, parent,
274 followdependents, skip_root)
275 except Exception, e:
276 if continueonfailure:
277 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
278 traceback.print_tb(sys.exc_info()[2])
279 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
280 pass
281 else:
282 if (ifupdownobj.ignore_error(str(e))):
283 pass
284 else:
285 raise Exception('error running iface %s (%s)'
286 %(ifacename, str(e)))
287
288 @classmethod
289 def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
290 dependency_graph=None, indegrees=None,
291 order=ifaceSchedulerFlags.POSTORDER,
292 followdependents=True):
293 """ Runs iface dependeny graph by visiting all the nodes
294
295 Parameters:
296 -----------
297 ifupdownobj : ifupdown object (used for getting and updating iface
298 object state)
299 dependency_graph : dependency graph in adjacency list
300 format (contains more than one dependency graph)
301 ops : list of operations to perform eg ['pre-up', 'up', 'post-up']
302
303 indegrees : indegree array if present is used to determine roots
304 of the graphs in the dependency_graph
305 """
306
307 if not ifupdownobj.ALL or not followdependents or len(ifacenames) == 1:
308 # If there is any interface that does exist, maybe it is a
309 # logical interface and we have to followupperifaces
310 followupperifaces = (True if
311 [i for i in ifacenames
312 if not ifupdownobj.link_exists(i)]
313 else False)
314 cls.run_iface_list(ifupdownobj, ifacenames, ops,
315 parent=None,order=order,
316 followdependents=followdependents)
317 if (not ifupdownobj.ALL and
318 (followdependents or followupperifaces) and 'up' in ops[0]):
319 # If user had given a set of interfaces to bring up
320 # try and execute 'up' on the upperifaces
321 ifupdownobj.logger.info('running upperifaces if available')
322 cls._STATE_CHECK = False
323 cls.run_iface_list_upper(ifupdownobj, ifacenames, ops,
324 skip_root=True)
325 cls._STATE_CHECK = True
326 return
327 run_queue = []
328
329 # Get a sorted list of all interfaces
330 if not indegrees:
331 indegrees = OrderedDict()
332 for ifacename in dependency_graph.keys():
333 indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
334 sorted_ifacenames = graph.topological_sort_graphs_all(dependency_graph,
335 dict(indegrees))
336 ifupdownobj.logger.debug('sorted ifacenames %s : '
337 %str(sorted_ifacenames))
338
339 # From the sorted list, pick interfaces that user asked
340 # and those that dont have any dependents first
341 [run_queue.append(ifacename)
342 for ifacename in sorted_ifacenames
343 if ifacename in ifacenames and
344 not indegrees.get(ifacename)]
345
346 ifupdownobj.logger.debug('graph roots (interfaces that dont have '
347 'dependents):' + ' %s' %str(run_queue))
348 cls.run_iface_list(ifupdownobj, run_queue, ops,
349 parent=None,order=order,
350 followdependents=followdependents)