]>
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
.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
))
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 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')
110 for ifaceobj
in ifaceobjs
:
111 ifaceobj
.status
= ifaceStatus
.SUCCESS
112 posthookfunc(ifupdownobj
, ifaceobj
, 'down')
115 # first run ifupdownobj handlers. This is good enough
116 # for the first object in the list
117 handler
= ifupdownobj
.ops_handlers
.get(op
)
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')
130 [posthookfunc(ifupdownobj
, ifaceobj
, ops
[0])
131 for ifaceobj
in ifaceobjs
]
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
139 Returns True or False indicating the caller to proceed with the
142 # proceed only for down operation
143 if 'down' not in ops
[0]:
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
)):
153 if ifaceobj
.type == ifaceType
.BRIDGE
:
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
162 # Long term this function should be replaced by
163 # walking the sysfs links to upper and lower device available in
166 if os
.listdir('/sys/class/net/%s/brif' %ifaceobj
.name
):
174 ulist
= ifaceobj
.upperifaces
178 # Get the list of upper ifaces other than the parent
179 tmpulist
= ([u
for u
in ulist
if u
!= parent
] if parent
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
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)
193 ifupdownobj
.logger
.warn('%s: skipping interface down,'
194 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
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 """
204 # Each ifacename can have a list of iface objects
205 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
207 raise Exception('%s: not found' %ifacename
)
209 for ifaceobj
in ifaceobjs
:
210 if not cls
._check
_upperifaces
(ifupdownobj
, ifaceobj
,
211 ops
, parent
, followdependents
):
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
)
218 for ifaceobj
in ifaceobjs
:
219 # Run lowerifaces or dependents
220 dlist
= ifaceobj
.lowerifaces
222 ifupdownobj
.logger
.debug('%s: found dependents %s'
223 %(ifacename
, str(dlist
)))
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
)]
234 cls
.run_iface_list(ifupdownobj
, new_dlist
, ops
,
235 ifacename
, order
, followdependents
,
236 continueonfailure
=False)
238 cls
.run_iface_list(ifupdownobj
, dlist
, ops
,
241 continueonfailure
=False)
243 if (ifupdownobj
.ignore_error(str(e
))):
246 # Dont bring the iface up if children did not come up
247 ifaceobj
.set_state_n_status(ifaceState
.NEW
,
250 if order
== ifaceSchedulerFlags
.POSTORDER
:
251 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
254 def run_iface_list(cls
, ifupdownobj
, ifacenames
,
255 ops
, parent
=None, order
=ifaceSchedulerFlags
.POSTORDER
,
256 followdependents
=True, continueonfailure
=True):
257 """ Runs interface list """
259 for ifacename
in ifacenames
:
261 cls
.run_iface_graph(ifupdownobj
, ifacename
, ops
, parent
,
262 order
, followdependents
)
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
)))
270 if (ifupdownobj
.ignore_error(str(e
))):
273 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
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 """
280 # Each ifacename can have a list of iface objects
281 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
283 raise Exception('%s: not found' %ifacename
)
286 # run the iface first and then its upperifaces
287 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
288 for ifaceobj
in ifaceobjs
:
290 ulist
= ifaceobj
.upperifaces
292 ifupdownobj
.logger
.debug('%s: found upperifaces %s'
293 %(ifacename
, str(ulist
)))
295 cls
.run_iface_list_upper(ifupdownobj
, ulist
, ops
,
298 continueonfailure
=True)
300 if (ifupdownobj
.ignore_error(str(e
))):
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 """
311 for ifacename
in ifacenames
:
313 cls
.run_iface_graph_upper(ifupdownobj
, ifacename
, ops
, parent
,
314 followdependents
, skip_root
)
316 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
317 traceback
.print_tb(sys
.exc_info()[2])
318 ifupdownobj
.logger
.warn('%s : %s' %(ifacename
, str(e
)))
322 def get_sorted_iface_list(cls
, ifupdownobj
, ifacenames
, ops
,
323 dependency_graph
, indegrees
=None):
324 if len(ifacenames
) == 1:
326 # Get a sorted list of all interfaces
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
335 return ifacenames_all_sorted
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
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.
353 **ifupdownobj** (object): ifupdownMain object
355 **ifacenames** (list): list of interface names
357 **ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
359 **dependency_graph** (dict): dependency graph in adjacency list format
362 **indegrees** (dict): indegree array of the dependency graph
364 **order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
366 **followdependents** (bool): follow dependent interfaces if true
371 # if ALL/auto interfaces are specified,
372 # - walk the dependency tree in postorder or inorder depending
374 # (This is to run interfaces correctly in order)
376 # - sort iface list if the ifaces belong to a "class"
377 # - else just run iface list in the order they were specified
379 # Run any upperifaces if available
381 followupperifaces
= False
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
)
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
)]
399 if not skip_ifacesort
and ifupdownobj
.IFACE_CLASS
:
400 # sort interfaces only if allow class was specified and
402 run_queue
= cls
.get_sorted_iface_list(ifupdownobj
, ifacenames
,
403 ops
, dependency_graph
, indegrees
)
404 if run_queue
and 'up' in ops
[0]:
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
,
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
))
423 ifupdownobj
.logger
.warn('interface sort returned None')
425 # If queue not present, just run interfaces that were asked by the user
427 run_queue
= list(ifacenames
)
432 cls
.run_iface_list(ifupdownobj
, run_queue
, ops
,
433 parent
=None, order
=order
,
434 followdependents
=followdependents
)
435 if not cls
._SCHED
_RETVAL
:
438 if (ifupdownobj
.config
.get('skip_upperifaces', '0') == '0' and
439 ((not ifupdownobj
.ALL
and followdependents
) or
440 followupperifaces
) and
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) ' +
446 cls
._STATE
_CHECK
= False
447 cls
.run_iface_list_upper(ifupdownobj
, ifacenames
, ops
,
449 cls
._STATE
_CHECK
= True