]>
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
, query_ifaceobj
=None,
54 """ Runs sub operation on an interface """
55 ifacename
= ifaceobj
.name
57 if (cls
._STATE
_CHECK
and
58 (ifaceobj
.state
>= ifaceState
.from_str(op
)) and
59 (ifaceobj
.status
== ifaceStatus
.SUCCESS
)):
60 ifupdownobj
.logger
.debug('%s: already in state %s' %(ifacename
, op
))
62 if not ifupdownobj
.ADDONS_ENABLE
: return
63 for mname
in ifupdownobj
.module_ops
.get(op
):
64 m
= ifupdownobj
.modules
.get(mname
)
68 msg
= ('%s: %s : running module %s' %(ifacename
, op
, mname
))
69 if op
== 'query-checkcurr':
70 # Dont check curr if the interface object was
72 if (ifaceobj
.priv_flags
& ifupdownobj
.NOCONFIG
):
74 ifupdownobj
.logger
.debug(msg
)
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 query_ifaceobj
=ifupdownobj
.create_n_save_ifaceobjcurr(
132 ifaceobj
) if op
== 'query-checkcurr' else None,
133 cenv
=ifupdownobj
.generate_running_env(ifaceobj
, op
)
134 if ifupdownobj
.COMPAT_EXEC_SCRIPTS
else None)
135 posthookfunc
= ifupdownobj
.sched_hooks
.get('posthook')
137 posthookfunc(ifupdownobj
, ifaceobj
, op
)
140 def _check_upperifaces(cls
, ifupdownobj
, ifaceobj
, ops
, parent
,
141 followdependents
=False):
142 """ Check if upperifaces are hanging off us and help caller decide
143 if he can proceed with the ops on this device
145 Returns True or False indicating the caller to proceed with the
148 # proceed only for down operation
149 if 'down' not in ops
[0]:
152 if (ifupdownobj
.FORCE
or
153 not ifupdownobj
.ADDONS_ENABLE
or
154 (not ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
) and
155 ifupdownobj
.config
.get('warn_on_ifdown', '0') == '0')):
158 ulist
= ifaceobj
.upperifaces
161 # Get the list of upper ifaces other than the parent
162 tmpulist
= ([u
for u
in ulist
if u
!= parent
] if parent
166 # XXX: This is expensive. Find a cheaper way to do this.
167 # if any of the upperdevs are present,
168 # return false to the caller to skip this interface
170 if ifupdownobj
.link_exists(u
):
171 if not ifupdownobj
.ALL
:
172 if ifupdownobj
.is_ifaceobj_noconfig(ifaceobj
):
173 ifupdownobj
.logger
.info('%s: skipping interface down,'
174 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
176 ifupdownobj
.logger
.warn('%s: skipping interface down,'
177 %ifaceobj
.name
+ ' upperiface %s still around ' %u)
182 def run_iface_graph(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
183 order
=ifaceSchedulerFlags
.POSTORDER
,
184 followdependents
=True):
185 """ runs interface by traversing all nodes rooted at itself """
187 # Each ifacename can have a list of iface objects
188 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
190 raise Exception('%s: not found' %ifacename
)
192 for ifaceobj
in ifaceobjs
:
193 if not cls
._check
_upperifaces
(ifupdownobj
, ifaceobj
,
194 ops
, parent
, followdependents
):
197 # If inorder, run the iface first and then its dependents
198 if order
== ifaceSchedulerFlags
.INORDER
:
199 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
201 for ifaceobj
in ifaceobjs
:
202 # Run lowerifaces or dependents
203 dlist
= ifaceobj
.lowerifaces
205 ifupdownobj
.logger
.debug('%s: found dependents %s'
206 %(ifacename
, str(dlist
)))
208 if not followdependents
:
209 # XXX: this is yet another extra step,
210 # but is needed for interfaces that are
211 # implicit dependents. even though we are asked to
212 # not follow dependents, we must follow the ones
213 # that dont have user given config. Because we own them
214 new_dlist
= [d
for d
in dlist
215 if ifupdownobj
.is_iface_noconfig(d
)]
217 cls
.run_iface_list(ifupdownobj
, new_dlist
, ops
,
218 ifacename
, order
, followdependents
,
219 continueonfailure
=False)
221 cls
.run_iface_list(ifupdownobj
, dlist
, ops
,
224 continueonfailure
=False)
226 if (ifupdownobj
.ignore_error(str(e
))):
229 # Dont bring the iface up if children did not come up
230 ifaceobj
.set_state_n_status(ifaceState
.NEW
,
233 if order
== ifaceSchedulerFlags
.POSTORDER
:
234 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
237 def run_iface_list(cls
, ifupdownobj
, ifacenames
,
238 ops
, parent
=None, order
=ifaceSchedulerFlags
.POSTORDER
,
239 followdependents
=True, continueonfailure
=True):
240 """ Runs interface list """
242 for ifacename
in ifacenames
:
244 cls
.run_iface_graph(ifupdownobj
, ifacename
, ops
, parent
,
245 order
, followdependents
)
247 if continueonfailure
:
248 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
249 traceback
.print_tb(sys
.exc_info()[2])
250 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
253 if (ifupdownobj
.ignore_error(str(e
))):
256 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
259 def run_iface_graph_upper(cls
, ifupdownobj
, ifacename
, ops
, parent
=None,
260 followdependents
=True, skip_root
=False):
261 """ runs interface by traversing all nodes rooted at itself """
263 # Each ifacename can have a list of iface objects
264 ifaceobjs
= ifupdownobj
.get_ifaceobjs(ifacename
)
266 raise Exception('%s: not found' %ifacename
)
269 # run the iface first and then its upperifaces
270 cls
.run_iface_list_ops(ifupdownobj
, ifaceobjs
, ops
)
271 for ifaceobj
in ifaceobjs
:
273 ulist
= ifaceobj
.upperifaces
275 ifupdownobj
.logger
.debug('%s: found upperifaces %s'
276 %(ifacename
, str(ulist
)))
278 cls
.run_iface_list_upper(ifupdownobj
, ulist
, ops
,
281 continueonfailure
=True)
283 if (ifupdownobj
.ignore_error(str(e
))):
289 def run_iface_list_upper(cls
, ifupdownobj
, ifacenames
,
290 ops
, parent
=None, followdependents
=True,
291 continueonfailure
=True, skip_root
=False):
292 """ Runs interface list """
294 for ifacename
in ifacenames
:
296 cls
.run_iface_graph_upper(ifupdownobj
, ifacename
, ops
, parent
,
297 followdependents
, skip_root
)
299 if continueonfailure
:
300 if ifupdownobj
.logger
.isEnabledFor(logging
.DEBUG
):
301 traceback
.print_tb(sys
.exc_info()[2])
302 ifupdownobj
.logger
.error('%s : %s' %(ifacename
, str(e
)))
305 if (ifupdownobj
.ignore_error(str(e
))):
308 raise Exception('%s : (%s)' %(ifacename
, str(e
)))
311 def sched_ifaces(cls
, ifupdownobj
, ifacenames
, ops
,
312 dependency_graph
=None, indegrees
=None,
313 order
=ifaceSchedulerFlags
.POSTORDER
,
314 followdependents
=True):
315 """ Runs iface dependeny graph by visiting all the nodes
319 ifupdownobj : ifupdown object (used for getting and updating iface
321 dependency_graph : dependency graph in adjacency list
322 format (contains more than one dependency graph)
323 ops : list of operations to perform eg ['pre-up', 'up', 'post-up']
325 indegrees : indegree array if present is used to determine roots
326 of the graphs in the dependency_graph
329 if not ifupdownobj
.ALL
or not followdependents
or len(ifacenames
) == 1:
330 # If there is any interface that does exist, maybe it is a
331 # logical interface and we have to followupperifaces
332 followupperifaces
= (True if
333 [i
for i
in ifacenames
334 if not ifupdownobj
.link_exists(i
)]
336 cls
.run_iface_list(ifupdownobj
, ifacenames
, ops
,
337 parent
=None,order
=order
,
338 followdependents
=followdependents
)
339 if (not ifupdownobj
.ALL
and
340 (followdependents
or followupperifaces
) and
342 # If user had given a set of interfaces to bring up
343 # try and execute 'up' on the upperifaces
344 ifupdownobj
.logger
.info('running upperifaces if available')
345 cls
._STATE
_CHECK
= False
346 cls
.run_iface_list_upper(ifupdownobj
, ifacenames
, ops
,
348 cls
._STATE
_CHECK
= True
352 # Get a sorted list of all interfaces
354 indegrees
= OrderedDict()
355 for ifacename
in dependency_graph
.keys():
356 indegrees
[ifacename
] = ifupdownobj
.get_iface_refcnt(ifacename
)
357 sorted_ifacenames
= graph
.topological_sort_graphs_all(dependency_graph
,
359 ifupdownobj
.logger
.debug('sorted ifacenames %s : '
360 %str
(sorted_ifacenames
))
362 # From the sorted list, pick interfaces that user asked
363 # and those that dont have any dependents first
364 [run_queue
.append(ifacename
)
365 for ifacename
in sorted_ifacenames
366 if ifacename
in ifacenames
and
367 not indegrees
.get(ifacename
)]
369 ifupdownobj
.logger
.debug('graph roots (interfaces that dont have '
370 'dependents):' + ' %s' %str
(run_queue
))
371 cls
.run_iface_list(ifupdownobj
, run_queue
, ops
,
372 parent
=None,order
=order
,
373 followdependents
=followdependents
)