]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown/scheduler.py
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
10 from statemanager
import *
13 from collections
import deque
14 from collections
import OrderedDict
19 from collections
import deque
20 from threading
import *
21 from ifupdownbase
import *
23 class ifaceSchedulerFlags():
24 """ Enumerates scheduler flags """
29 class ifaceScheduler():
30 """ scheduler functions to schedule configuration of interfaces.
32 supports scheduling of interfaces serially in plain interface list
33 or dependency graph format.
42 def run_iface_op(cls
, ifupdownobj
, ifaceobj
, op
, cenv
=None):
43 """ Runs sub operation on an interface """
44 ifacename
= ifaceobj
.name
46 if (cls
._STATE
_CHECK
and
47 (ifaceobj
.state
>= ifaceState
.from_str(op
))):
48 ifupdownobj
.logger
.debug('%s: already in state %s' %(ifacename
, op
))
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
)
58 msg
= ('%s: %s : running module %s' %(ifacename
, op
, mname
))
59 if op
== 'query-checkcurr':
60 # Dont check curr if the interface object was
62 if (ifaceobj
.priv_flags
& ifupdownobj
.NOCONFIG
):
64 ifupdownobj
.logger
.debug(msg
)
65 m
.run(ifaceobj
, op
, query_ifaceobj
)
67 ifupdownobj
.logger
.debug(msg
)
71 ifupdownobj
.log_error(str(e
))
72 err
= 0 # error can be ignored by log_error, in which case
75 if err
or ifaceobj
.status
== ifaceStatus
.ERROR
:
76 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
78 if 'up' in op
or 'down' in op
:
79 cls
._SCHED
_RETVAL
= False
81 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
84 if ifupdownobj
.COMPAT_EXEC_SCRIPTS
:
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
))
90 ifupdownobj
.exec_command(mname
, cmdenv
=cenv
)
92 ifupdownobj
.log_error(str(e
))
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
99 # minor optimization. If operation is 'down', proceed only
100 # if interface exists in the system
101 ifacename
= ifaceobjs
[0].name
102 if ('down' in ops
[0] and
103 not ifupdownobj
.link_exists(ifacename
)):
104 ifupdownobj
.logger
.debug('%s: does not exist' %ifacename
)
105 # run posthook before you get out of here, so that
106 # appropriate cleanup is done
107 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
109 for ifaceobj
in ifaceobjs
:
110 ifaceobj
.status
= ifaceStatus
.SUCCESS
111 posthookfunc(ifupdownobj
, ifaceobj
, 'down')
114 # first run ifupdownobj handlers. This is good enough
115 # for the first object in the list
116 handler
= ifupdownobj
.ops_handlers
.get(op
)
118 if (not ifaceobjs
[0].addr_method
or
119 (ifaceobjs
[0].addr_method
and
120 ifaceobjs
[0].addr_method
!= 'manual')):
121 handler(ifupdownobj
, ifaceobjs
[0])
122 for ifaceobj
in ifaceobjs
:
123 cls
.run_iface_op(ifupdownobj
, ifaceobj
, op
,
124 cenv
=ifupdownobj
.generate_running_env(ifaceobj
, op
)
125 if ifupdownobj
.COMPAT_EXEC_SCRIPTS
else None)
126 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
128 posthookfunc(ifupdownobj
, ifaceobj
, op
)
131 def _check_upperifaces(cls
, ifupdownobj
, ifaceobj
, ops
, parent
,
132 followdependents
=False):
133 """ Check if upperifaces are hanging off us and help caller decide
134 if he can proceed with the ops on this device
136 Returns True or False indicating the caller to proceed with the
139 # proceed only for down operation
140 if 'down' not in ops
[0]:
143 if (ifupdownobj
.FORCE
or
144 not ifupdownobj
.ADDONS_ENABLE
or
145 (not ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
) and
146 ifupdownobj
.config
.get('warn_on_ifdown', '0') == '0' and
147 not ifupdownobj
.ALL
)):
150 ulist
= ifaceobj
.upperifaces
154 # Get the list of upper ifaces other than the parent
155 tmpulist
= ([u
for u
in ulist
if u
!= parent
] if parent
159 # XXX: This is expensive. Find a cheaper way to do this.
160 # if any of the upperdevs are present,
161 # return false to the caller to skip this interface
163 if ifupdownobj
.link_exists(u
):
164 if not ifupdownobj
.ALL
:
165 if ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
):
166 ifupdownobj
.logger
.info('%s: skipping interface down,'
167 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
169 ifupdownobj
.logger
.warn('%s: skipping interface down,'
170 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
175 def run_iface_graph(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
176 order
=ifaceSchedulerFlags
.POSTORDER
,
177 followdependents
=True):
178 """ runs interface by traversing all nodes rooted at itself """
180 # Each ifacename can have a list of iface objects
181 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
183 raise Exception('%s: not found' %ifacename
)
185 for ifaceobj
in ifaceobjs
:
186 if not cls
._check
_upperifaces
(ifupdownobj
, ifaceobj
,
187 ops
, parent
, followdependents
):
190 # If inorder, run the iface first and then its dependents
191 if order
== ifaceSchedulerFlags
.INORDER
:
192 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
194 for ifaceobj
in ifaceobjs
:
195 # Run lowerifaces or dependents
196 dlist
= ifaceobj
.lowerifaces
198 ifupdownobj
.logger
.debug('%s: found dependents %s'
199 %(ifacename
, str(dlist
)))
201 if not followdependents
:
202 # XXX: this is yet another extra step,
203 # but is needed for interfaces that are
204 # implicit dependents. even though we are asked to
205 # not follow dependents, we must follow the ones
206 # that dont have user given config. Because we own them
207 new_dlist
= [d
for d
in dlist
208 if ifupdownobj
.is_iface_noconfig(d
)]
210 cls
.run_iface_list(ifupdownobj
, new_dlist
, ops
,
211 ifacename
, order
, followdependents
,
212 continueonfailure
=False)
214 cls
.run_iface_list(ifupdownobj
, dlist
, ops
,
217 continueonfailure
=False)
219 if (ifupdownobj
.ignore_error(str(e
))):
222 # Dont bring the iface up if children did not come up
223 ifaceobj
.set_state_n_status(ifaceState
.NEW
,
226 if order
== ifaceSchedulerFlags
.POSTORDER
:
227 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
230 def run_iface_list(cls
, ifupdownobj
, ifacenames
,
231 ops
, parent
=None, order
=ifaceSchedulerFlags
.POSTORDER
,
232 followdependents
=True, continueonfailure
=True):
233 """ Runs interface list """
235 for ifacename
in ifacenames
:
237 cls
.run_iface_graph(ifupdownobj
, ifacename
, ops
, parent
,
238 order
, followdependents
)
240 if continueonfailure
:
241 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
242 traceback
.print_tb(sys
.exc_info()[2])
243 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
246 if (ifupdownobj
.ignore_error(str(e
))):
249 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
252 def run_iface_graph_upper(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
253 followdependents
=True, skip_root
=False):
254 """ runs interface by traversing all nodes rooted at itself """
256 # Each ifacename can have a list of iface objects
257 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
259 raise Exception('%s: not found' %ifacename
)
262 # run the iface first and then its upperifaces
263 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
264 for ifaceobj
in ifaceobjs
:
266 ulist
= ifaceobj
.upperifaces
268 ifupdownobj
.logger
.debug('%s: found upperifaces %s'
269 %(ifacename
, str(ulist
)))
271 cls
.run_iface_list_upper(ifupdownobj
, ulist
, ops
,
274 continueonfailure
=True)
276 if (ifupdownobj
.ignore_error(str(e
))):
282 def run_iface_list_upper(cls
, ifupdownobj
, ifacenames
,
283 ops
, parent
=None, followdependents
=True,
284 continueonfailure
=True, skip_root
=False):
285 """ Runs interface list """
287 for ifacename
in ifacenames
:
289 cls
.run_iface_graph_upper(ifupdownobj
, ifacename
, ops
, parent
,
290 followdependents
, skip_root
)
292 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
293 traceback
.print_tb(sys
.exc_info()[2])
294 ifupdownobj
.logger
.warn('%s : %s' %(ifacename
, str(e
)))
298 def get_sorted_iface_list(cls
, ifupdownobj
, ifacenames
, ops
,
299 dependency_graph
, indegrees
=None):
300 if len(ifacenames
) == 1:
302 # Get a sorted list of all interfaces
304 indegrees
= OrderedDict()
305 for ifacename
in dependency_graph
.keys():
306 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
307 ifacenames_all_sorted
= graph
.topological_sort_graphs_all(
308 dependency_graph
, indegrees
)
309 # if ALL was set, return all interfaces
311 return ifacenames_all_sorted
313 # else return ifacenames passed as argument in sorted order
314 ifacenames_sorted
= []
315 [ifacenames_sorted
.append(ifacename
)
316 for ifacename
in ifacenames_all_sorted
317 if ifacename
in ifacenames
]
318 return ifacenames_sorted
321 def sched_ifaces(cls
, ifupdownobj
, ifacenames
, ops
,
322 dependency_graph
=None, indegrees
=None,
323 order
=ifaceSchedulerFlags
.POSTORDER
,
324 followdependents
=True):
325 """ runs interface configuration modules on interfaces passed as
326 argument. Runs topological sort on interface dependency graph.
329 **ifupdownobj** (object): ifupdownMain object
331 **ifacenames** (list): list of interface names
333 **ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
335 **dependency_graph** (dict): dependency graph in adjacency list format
338 **indegrees** (dict): indegree array of the dependency graph
340 **order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
342 **followdependents** (bool): follow dependent interfaces if true
347 # if ALL/auto interfaces are specified,
348 # - walk the dependency tree in postorder or inorder depending
350 # (This is to run interfaces correctly in order)
352 # - sort iface list if the ifaces belong to a "class"
353 # - else just run iface list in the order they were specified
355 # Run any upperifaces if available
357 followupperifaces
= []
359 skip_ifacesort
= int(ifupdownobj
.config
.get('skip_ifacesort', '0'))
360 if not skip_ifacesort
and not indegrees
:
361 indegrees
= OrderedDict()
362 for ifacename
in dependency_graph
.keys():
363 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
365 if not ifupdownobj
.ALL
:
366 # If there is any interface that does exist, maybe it is a
367 # logical interface and we have to followupperifaces when it
368 # comes up, so get that list.
369 followupperifaces
= (True if
370 [i
for i
in ifacenames
371 if not ifupdownobj
.link_exists(i
)]
373 if not skip_ifacesort
and ifupdownobj
.IFACE_CLASS
:
374 # sort interfaces only if allow class was specified and
376 run_queue
= cls
.get_sorted_iface_list(ifupdownobj
, ifacenames
,
377 ops
, dependency_graph
, indegrees
)
378 if run_queue
and 'up' in ops
[0]:
381 # if -a is set, we dont really have to sort. We pick the interfaces
382 # that have no parents and
383 if not skip_ifacesort
:
384 sorted_ifacenames
= cls
.get_sorted_iface_list(ifupdownobj
,
385 ifacenames
, ops
, dependency_graph
,
387 if sorted_ifacenames
:
388 # pick interfaces that user asked
389 # and those that dont have any dependents first
390 [run_queue
.append(ifacename
)
391 for ifacename
in sorted_ifacenames
392 if ifacename
in ifacenames
and
393 not indegrees
.get(ifacename
)]
394 ifupdownobj
.logger
.debug('graph roots (interfaces that ' +
395 'dont have dependents):' + ' %s' %str
(run_queue
))
397 ifupdownobj
.logger
.warn('interface sort returned None')
399 # If queue not present, just run interfaces that were asked by the user
401 run_queue
= list(ifacenames
)
406 ifupdownobj
.logger
.info('running interfaces: %s' %str
(run_queue
))
407 cls
.run_iface_list(ifupdownobj
, run_queue
, ops
,
408 parent
=None, order
=order
,
409 followdependents
=followdependents
)
410 if not cls
._SCHED
_RETVAL
:
413 if (ifupdownobj
.config
.get('skip_upperifaces', '0') == '0' and
414 ((not ifupdownobj
.ALL
and followdependents
) or
415 followupperifaces
) and
417 # If user had given a set of interfaces to bring up
418 # try and execute 'up' on the upperifaces
419 ifupdownobj
.logger
.info('running upperifaces (parent interfaces) ' +
421 cls
._STATE
_CHECK
= False
422 cls
.run_iface_list_upper(ifupdownobj
, ifacenames
, ops
,
424 cls
._STATE
_CHECK
= True