]> git.proxmox.com Git - mirror_ifupdown2.git/blob - pkg/scheduler.py
Cleanup saved state for down objects on ifdown
[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, ops[0])
114
115 @classmethod
116 def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
117 followdependents=False):
118 """ Check if conflicting upper ifaces are around and warn if required
119
120 Returns False if this interface needs to be skipped,
121 else return True """
122
123 # XXX: simply return for now, the warnings this function prints
124 # are very confusing. Get rid of this function soon
125 return True
126
127 if 'up' in ops[0] and followdependents:
128 return True
129
130 # Deal with upperdevs first
131 ulist = ifaceobj.upperifaces
132 if ulist:
133 tmpulist = ([u for u in ulist if u != parent] if parent
134 else ulist)
135 if not tmpulist:
136 return True
137 if 'down' in ops[0]:
138 # XXX: This is expensive. Find a cheaper way to do this
139 # if any of the upperdevs are present,
140 # dont down this interface
141 for u in tmpulist:
142 if ifupdownobj.link_exists(u):
143 if not ifupdownobj.FORCE and not ifupdownobj.ALL:
144 ifupdownobj.logger.warn('%s: ' %ifaceobj.name +
145 'upperiface %s still around' %u)
146 return True
147 elif 'up' in ops[0] and not ifupdownobj.ALL:
148 # For 'up', just warn that there is an upperdev which is
149 # probably not up
150 for u in tmpulist:
151 if not ifupdownobj.link_exists(u):
152 ifupdownobj.logger.warn('%s: ' %ifaceobj.name +
153 'upper iface %s does not exist' %u)
154 return True
155
156 @classmethod
157 def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
158 order=ifaceSchedulerFlags.POSTORDER,
159 followdependents=True):
160 """ runs interface by traversing all nodes rooted at itself """
161
162 # Each ifacename can have a list of iface objects
163 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
164 if not ifaceobjs:
165 raise Exception('%s: not found' %ifacename)
166
167 for ifaceobj in ifaceobjs:
168 if not cls._check_upperifaces(ifupdownobj, ifaceobj,
169 ops, parent, followdependents):
170 return
171 if order == ifaceSchedulerFlags.INORDER:
172 # If inorder, run the iface first and then its dependents
173 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
174
175 # Run lowerifaces or dependents
176 dlist = ifaceobj.lowerifaces
177 if dlist:
178 ifupdownobj.logger.debug('%s: found dependents %s'
179 %(ifacename, str(dlist)))
180 try:
181 if not followdependents:
182 # XXX: this is yet another extra step,
183 # but is needed for interfaces that are
184 # implicit dependents. even though we are asked to
185 # not follow dependents, we must follow the ones
186 # that dont have user given config. Because we own them
187 new_dlist = [d for d in dlist
188 if ifupdownobj.is_iface_noconfig(d)]
189 if new_dlist:
190 cls.run_iface_list(ifupdownobj, new_dlist, ops,
191 ifacename, order,
192 followdependents,
193 continueonfailure=False)
194 else:
195 cls.run_iface_list(ifupdownobj, dlist, ops,
196 ifacename, order,
197 followdependents,
198 continueonfailure=False)
199 except Exception, e:
200 if (ifupdownobj.ignore_error(str(e))):
201 pass
202 else:
203 # Dont bring the iface up if children did not come up
204 ifaceobj.set_state_n_status(ifaceState.NEW,
205 ifaceStatus.ERROR)
206 raise
207 if order == ifaceSchedulerFlags.POSTORDER:
208 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
209
210 @classmethod
211 def run_iface_list(cls, ifupdownobj, ifacenames,
212 ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
213 followdependents=True, continueonfailure=True):
214 """ Runs interface list """
215
216 for ifacename in ifacenames:
217 try:
218 cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
219 order, followdependents)
220 except Exception, e:
221 if continueonfailure:
222 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
223 traceback.print_tb(sys.exc_info()[2])
224 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
225 pass
226 else:
227 if (ifupdownobj.ignore_error(str(e))):
228 pass
229 else:
230 raise Exception('%s : (%s)' %(ifacename, str(e)))
231
232 @classmethod
233 def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
234 followdependents=True, skip_root=False):
235 """ runs interface by traversing all nodes rooted at itself """
236
237 # Each ifacename can have a list of iface objects
238 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
239 if not ifaceobjs:
240 raise Exception('%s: not found' %ifacename)
241
242 for ifaceobj in ifaceobjs:
243 if not skip_root:
244 # run the iface first and then its upperifaces
245 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
246
247 # Run upperifaces
248 ulist = ifaceobj.upperifaces
249 if ulist:
250 ifupdownobj.logger.debug('%s: found upperifaces %s'
251 %(ifacename, str(ulist)))
252 try:
253 cls.run_iface_list_upper(ifupdownobj, ulist, ops,
254 ifacename,
255 followdependents,
256 continueonfailure=True)
257 except Exception, e:
258 if (ifupdownobj.ignore_error(str(e))):
259 pass
260 else:
261 raise
262
263 @classmethod
264 def run_iface_list_upper(cls, ifupdownobj, ifacenames,
265 ops, parent=None, followdependents=True,
266 continueonfailure=True, skip_root=False):
267 """ Runs interface list """
268
269 for ifacename in ifacenames:
270 try:
271 cls.run_iface_graph_upper(ifupdownobj, ifacename, ops, parent,
272 followdependents, skip_root)
273 except Exception, e:
274 if continueonfailure:
275 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
276 traceback.print_tb(sys.exc_info()[2])
277 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
278 pass
279 else:
280 if (ifupdownobj.ignore_error(str(e))):
281 pass
282 else:
283 raise Exception('%s : (%s)' %(ifacename, str(e)))
284
285 @classmethod
286 def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
287 dependency_graph=None, indegrees=None,
288 order=ifaceSchedulerFlags.POSTORDER,
289 followdependents=True):
290 """ Runs iface dependeny graph by visiting all the nodes
291
292 Parameters:
293 -----------
294 ifupdownobj : ifupdown object (used for getting and updating iface
295 object state)
296 dependency_graph : dependency graph in adjacency list
297 format (contains more than one dependency graph)
298 ops : list of operations to perform eg ['pre-up', 'up', 'post-up']
299
300 indegrees : indegree array if present is used to determine roots
301 of the graphs in the dependency_graph
302 """
303
304 if not ifupdownobj.ALL or not followdependents or len(ifacenames) == 1:
305 # If there is any interface that does exist, maybe it is a
306 # logical interface and we have to followupperifaces
307 followupperifaces = (True if
308 [i for i in ifacenames
309 if not ifupdownobj.link_exists(i)]
310 else False)
311 cls.run_iface_list(ifupdownobj, ifacenames, ops,
312 parent=None,order=order,
313 followdependents=followdependents)
314 if (not ifupdownobj.ALL and
315 (followdependents or followupperifaces) and 'up' in ops[0]):
316 # If user had given a set of interfaces to bring up
317 # try and execute 'up' on the upperifaces
318 ifupdownobj.logger.info('running upperifaces if available')
319 cls._STATE_CHECK = False
320 cls.run_iface_list_upper(ifupdownobj, ifacenames, ops,
321 skip_root=True)
322 cls._STATE_CHECK = True
323 return
324 run_queue = []
325
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 sorted_ifacenames = graph.topological_sort_graphs_all(dependency_graph,
332 dict(indegrees))
333 ifupdownobj.logger.debug('sorted ifacenames %s : '
334 %str(sorted_ifacenames))
335
336 # From the sorted list, pick interfaces that user asked
337 # and those that dont have any dependents first
338 [run_queue.append(ifacename)
339 for ifacename in sorted_ifacenames
340 if ifacename in ifacenames and
341 not indegrees.get(ifacename)]
342
343 ifupdownobj.logger.debug('graph roots (interfaces that dont have '
344 'dependents):' + ' %s' %str(run_queue))
345 cls.run_iface_list(ifupdownobj, run_queue, ops,
346 parent=None,order=order,
347 followdependents=followdependents)