]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - pkg/scheduler.py
3 # Copyright 2013. Cumulus Networks, Inc.
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():
27 class ifaceScheduler():
28 """ scheduler functions to schedule configuration of interfaces.
30 supports scheduling of interfaces serially in plain interface list
31 or dependency graph format.
34 - run topological sort on the iface objects
35 - In the sorted iface object list, pick up interfaces with no parents
36 and run ops on them and their children.
37 - If operation is up and user gave interface list (ie not all)
38 option, also see if there were upper-devices and run ops on them.
39 - if operation is down, dont down the interface if it still
40 has upperifaces present. The down operation is executed when the
41 last upperiface goes away. If force option is set, this rule does not
43 - run ops calls addon modules run operation passing the iface
44 object and op to each module.
45 - ops are [pre-up, up, post-up, pre-down, down,
46 post-down, query-running, query-check]
52 def run_iface_op(cls
, ifupdownobj
, ifaceobj
, op
, cenv
=None):
53 """ Runs sub operation on an interface """
54 ifacename
= ifaceobj
.name
56 if (cls
._STATE
_CHECK
and
57 (ifaceobj
.state
>= ifaceState
.from_str(op
)) and
58 (ifaceobj
.status
== ifaceStatus
.SUCCESS
)):
59 ifupdownobj
.logger
.debug('%s: already in state %s' %(ifacename
, op
))
61 if not ifupdownobj
.ADDONS_ENABLE
: return
62 if op
== 'query-checkcurr':
63 query_ifaceobj
=ifupdownobj
.create_n_save_ifaceobjcurr(ifaceobj
)
64 for mname
in ifupdownobj
.module_ops
.get(op
):
65 m
= ifupdownobj
.modules
.get(mname
)
69 msg
= ('%s: %s : running module %s' %(ifacename
, op
, mname
))
70 if op
== 'query-checkcurr':
71 # Dont check curr if the interface object was
73 if (ifaceobj
.priv_flags
& ifupdownobj
.NOCONFIG
):
75 ifupdownobj
.logger
.debug(msg
)
76 m
.run(ifaceobj
, op
, query_ifaceobj
)
78 ifupdownobj
.logger
.debug(msg
)
82 ifupdownobj
.log_error(str(e
))
85 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
88 ifaceobj
.set_state_n_status(ifaceState
.from_str(op
),
91 if ifupdownobj
.COMPAT_EXEC_SCRIPTS
:
92 # execute /etc/network/ scripts
93 for mname
in ifupdownobj
.script_ops
.get(op
, []):
94 ifupdownobj
.logger
.debug('%s: %s : running script %s'
95 %(ifacename
, op
, mname
))
97 ifupdownobj
.exec_command(mname
, cmdenv
=cenv
)
99 ifupdownobj
.log_error(str(e
))
102 def run_iface_list_ops(cls
, ifupdownobj
, ifaceobjs
, ops
):
103 """ Runs all operations on a list of interface
104 configurations for the same interface
106 # minor optimization. If operation is 'down', proceed only
107 # if interface exists in the system
108 ifacename
= ifaceobjs
[0].name
109 if ('down' in ops
[0] and
110 not ifupdownobj
.link_exists(ifacename
)):
111 ifupdownobj
.logger
.debug('%s: does not exist' %ifacename
)
112 # run posthook before you get out of here, so that
113 # appropriate cleanup is done
114 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
116 for ifaceobj
in ifaceobjs
:
117 ifaceobj
.status
= ifaceStatus
.SUCCESS
118 posthookfunc(ifupdownobj
, ifaceobj
, 'down')
121 # first run ifupdownobj handlers. This is good enough
122 # for the first object in the list
123 handler
= ifupdownobj
.ops_handlers
.get(op
)
125 if (not ifaceobjs
[0].addr_method
or
126 (ifaceobjs
[0].addr_method
and
127 ifaceobjs
[0].addr_method
!= 'manual')):
128 handler(ifupdownobj
, ifaceobjs
[0])
129 for ifaceobj
in ifaceobjs
:
130 cls
.run_iface_op(ifupdownobj
, ifaceobj
, op
,
131 cenv
=ifupdownobj
.generate_running_env(ifaceobj
, op
)
132 if ifupdownobj
.COMPAT_EXEC_SCRIPTS
else None)
133 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
135 posthookfunc(ifupdownobj
, ifaceobj
, op
)
138 def _check_upperifaces(cls
, ifupdownobj
, ifaceobj
, ops
, parent
,
139 followdependents
=False):
140 """ Check if upperifaces are hanging off us and help caller decide
141 if he can proceed with the ops on this device
143 Returns True or False indicating the caller to proceed with the
146 # proceed only for down operation
147 if 'down' not in ops
[0]:
150 if (ifupdownobj
.FORCE
or
151 not ifupdownobj
.ADDONS_ENABLE
or
152 (not ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
) and
153 ifupdownobj
.config
.get('warn_on_ifdown', '0') == '0')):
156 ulist
= ifaceobj
.upperifaces
159 # Get the list of upper ifaces other than the parent
160 tmpulist
= ([u
for u
in ulist
if u
!= parent
] if parent
164 # XXX: This is expensive. Find a cheaper way to do this.
165 # if any of the upperdevs are present,
166 # return false to the caller to skip this interface
168 if ifupdownobj
.link_exists(u
):
169 if not ifupdownobj
.ALL
:
170 if ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
):
171 ifupdownobj
.logger
.info('%s: skipping interface down,'
172 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
174 ifupdownobj
.logger
.warn('%s: skipping interface down,'
175 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
180 def run_iface_graph(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
181 order
=ifaceSchedulerFlags
.POSTORDER
,
182 followdependents
=True):
183 """ runs interface by traversing all nodes rooted at itself """
185 # Each ifacename can have a list of iface objects
186 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
188 raise Exception('%s: not found' %ifacename
)
190 for ifaceobj
in ifaceobjs
:
191 if not cls
._check
_upperifaces
(ifupdownobj
, ifaceobj
,
192 ops
, parent
, followdependents
):
195 # If inorder, run the iface first and then its dependents
196 if order
== ifaceSchedulerFlags
.INORDER
:
197 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
199 for ifaceobj
in ifaceobjs
:
200 # Run lowerifaces or dependents
201 dlist
= ifaceobj
.lowerifaces
203 ifupdownobj
.logger
.debug('%s: found dependents %s'
204 %(ifacename
, str(dlist
)))
206 if not followdependents
:
207 # XXX: this is yet another extra step,
208 # but is needed for interfaces that are
209 # implicit dependents. even though we are asked to
210 # not follow dependents, we must follow the ones
211 # that dont have user given config. Because we own them
212 new_dlist
= [d
for d
in dlist
213 if ifupdownobj
.is_iface_noconfig(d
)]
215 cls
.run_iface_list(ifupdownobj
, new_dlist
, ops
,
216 ifacename
, order
, followdependents
,
217 continueonfailure
=False)
219 cls
.run_iface_list(ifupdownobj
, dlist
, ops
,
222 continueonfailure
=False)
224 if (ifupdownobj
.ignore_error(str(e
))):
227 # Dont bring the iface up if children did not come up
228 ifaceobj
.set_state_n_status(ifaceState
.NEW
,
231 if order
== ifaceSchedulerFlags
.POSTORDER
:
232 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
235 def run_iface_list(cls
, ifupdownobj
, ifacenames
,
236 ops
, parent
=None, order
=ifaceSchedulerFlags
.POSTORDER
,
237 followdependents
=True, continueonfailure
=True):
238 """ Runs interface list """
240 for ifacename
in ifacenames
:
242 cls
.run_iface_graph(ifupdownobj
, ifacename
, ops
, parent
,
243 order
, followdependents
)
245 if continueonfailure
:
246 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
247 traceback
.print_tb(sys
.exc_info()[2])
248 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
251 if (ifupdownobj
.ignore_error(str(e
))):
254 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
257 def run_iface_graph_upper(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
258 followdependents
=True, skip_root
=False):
259 """ runs interface by traversing all nodes rooted at itself """
261 # Each ifacename can have a list of iface objects
262 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
264 raise Exception('%s: not found' %ifacename
)
267 # run the iface first and then its upperifaces
268 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
269 for ifaceobj
in ifaceobjs
:
271 ulist
= ifaceobj
.upperifaces
273 ifupdownobj
.logger
.debug('%s: found upperifaces %s'
274 %(ifacename
, str(ulist
)))
276 cls
.run_iface_list_upper(ifupdownobj
, ulist
, ops
,
279 continueonfailure
=True)
281 if (ifupdownobj
.ignore_error(str(e
))):
287 def run_iface_list_upper(cls
, ifupdownobj
, ifacenames
,
288 ops
, parent
=None, followdependents
=True,
289 continueonfailure
=True, skip_root
=False):
290 """ Runs interface list """
292 for ifacename
in ifacenames
:
294 cls
.run_iface_graph_upper(ifupdownobj
, ifacename
, ops
, parent
,
295 followdependents
, skip_root
)
297 if continueonfailure
:
298 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
299 traceback
.print_tb(sys
.exc_info()[2])
300 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
303 if (ifupdownobj
.ignore_error(str(e
))):
306 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
309 def sched_ifaces(cls
, ifupdownobj
, ifacenames
, ops
,
310 dependency_graph
=None, indegrees
=None,
311 order
=ifaceSchedulerFlags
.POSTORDER
,
312 followdependents
=True):
313 """ Runs iface dependeny graph by visiting all the nodes
317 ifupdownobj : ifupdown object (used for getting and updating iface
319 dependency_graph : dependency graph in adjacency list
320 format (contains more than one dependency graph)
321 ops : list of operations to perform eg ['pre-up', 'up', 'post-up']
323 indegrees : indegree array if present is used to determine roots
324 of the graphs in the dependency_graph
327 if not ifupdownobj
.ALL
or not followdependents
or len(ifacenames
) == 1:
328 # If there is any interface that does exist, maybe it is a
329 # logical interface and we have to followupperifaces
330 followupperifaces
= (True if
331 [i
for i
in ifacenames
332 if not ifupdownobj
.link_exists(i
)]
334 cls
.run_iface_list(ifupdownobj
, ifacenames
, ops
,
335 parent
=None,order
=order
,
336 followdependents
=followdependents
)
337 if (not ifupdownobj
.ALL
and
338 (followdependents
or followupperifaces
) and
340 # If user had given a set of interfaces to bring up
341 # try and execute 'up' on the upperifaces
342 ifupdownobj
.logger
.info('running upperifaces if available')
343 cls
._STATE
_CHECK
= False
344 cls
.run_iface_list_upper(ifupdownobj
, ifacenames
, ops
,
346 cls
._STATE
_CHECK
= True
349 if ifupdownobj
.config
.get('skip_ifacesort', '0') == '1':
350 # This is a backdoor to skip sorting of interfaces, if required
351 cls
.run_iface_list(ifupdownobj
, ifacenames
, ops
,
352 parent
=None,order
=order
,
353 followdependents
=followdependents
)
357 # Get a sorted list of all interfaces
359 indegrees
= OrderedDict()
360 for ifacename
in dependency_graph
.keys():
361 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
362 sorted_ifacenames
= graph
.topological_sort_graphs_all(dependency_graph
,
364 ifupdownobj
.logger
.debug('sorted ifacenames %s : '
365 %str
(sorted_ifacenames
))
367 # From the sorted list, pick interfaces that user asked
368 # and those that dont have any dependents first
369 [run_queue
.append(ifacename
)
370 for ifacename
in sorted_ifacenames
371 if ifacename
in ifacenames
and
372 not indegrees
.get(ifacename
)]
374 ifupdownobj
.logger
.debug('graph roots (interfaces that dont have '
375 'dependents):' + ' %s' %str
(run_queue
))
376 cls
.run_iface_list(ifupdownobj
, run_queue
, ops
,
377 parent
=None,order
=order
,
378 followdependents
=followdependents
)