]> git.proxmox.com Git - mirror_ifupdown2.git/blame - pkg/scheduler.py
remove native ifupdown support from bridge-utils, vlan, ifenslave and
[mirror_ifupdown2.git] / pkg / scheduler.py
CommitLineData
a6f80f0e 1#!/usr/bin/python
3e8ee54f 2#
3# Copyright 2013. Cumulus Networks, Inc.
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6# ifaceScheduler --
7# interface scheduler
8#
a6f80f0e 9
a6f80f0e 10from statemanager import *
11from iface import *
12from graph import *
13from collections import deque
14from collections import OrderedDict
a6f80f0e 15import logging
d08d5f54 16import traceback
69f58278 17import sys
a6f80f0e 18from graph import *
19from collections import deque
20from threading import *
21from ifupdownbase import *
22
d08d5f54 23class ifaceSchedulerFlags():
c798b0f4 24 INORDER = 0x1
25 POSTORDER = 0x2
d08d5f54 26
be0b20f2 27class ifaceScheduler():
28 """ scheduler functions to schedule configuration of interfaces.
a6f80f0e 29
a6f80f0e 30 supports scheduling of interfaces serially in plain interface list
31 or dependency graph format.
99b212b0 32
33 Algo:
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
42 apply.
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]
a6f80f0e 47 """
48
c798b0f4 49 _STATE_CHECK = True
50
be0b20f2 51 @classmethod
923290bd 52 def run_iface_op(cls, ifupdownobj, ifaceobj, op, query_ifaceobj=None,
53 cenv=None):
a6f80f0e 54 """ Runs sub operation on an interface """
62ddec8b 55 ifacename = ifaceobj.name
a6f80f0e 56
c798b0f4 57 if (cls._STATE_CHECK and
62ddec8b 58 (ifaceobj.state >= ifaceState.from_str(op)) and
59 (ifaceobj.status == ifaceStatus.SUCCESS)):
be0b20f2 60 ifupdownobj.logger.debug('%s: already in state %s' %(ifacename, op))
d08d5f54 61 return
20dd6242 62 if not ifupdownobj.ADDONS_ENABLE: return
be0b20f2 63 for mname in ifupdownobj.module_ops.get(op):
37c0543d 64 m = ifupdownobj.modules.get(mname)
a6f80f0e 65 err = 0
66 try:
d08d5f54 67 if hasattr(m, 'run'):
a690dfae 68 msg = ('%s: %s : running module %s' %(ifacename, op, mname))
739f665b 69 if op == 'query-checkcurr':
d08d5f54 70 # Dont check curr if the interface object was
37c0543d 71 # auto generated
d08d5f54 72 if (ifaceobj.priv_flags & ifupdownobj.NOCONFIG):
37c0543d 73 continue
a690dfae 74 ifupdownobj.logger.debug(msg)
d08d5f54 75 m.run(ifaceobj, op,
923290bd 76 query_ifaceobj)
a6f80f0e 77 else:
a690dfae 78 ifupdownobj.logger.debug(msg)
d08d5f54 79 m.run(ifaceobj, op)
a6f80f0e 80 except Exception, e:
81 err = 1
be0b20f2 82 ifupdownobj.log_error(str(e))
a6f80f0e 83 finally:
31a5f4c3 84 if err:
85 ifaceobj.set_state_n_status(ifaceState.from_str(op),
86 ifaceStatus.ERROR)
d08d5f54 87 else:
31a5f4c3 88 ifaceobj.set_state_n_status(ifaceState.from_str(op),
89 ifaceStatus.SUCCESS)
6bd7fc74 90
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'
d08d5f54 95 %(ifacename, op, mname))
6bd7fc74 96 try:
97 ifupdownobj.exec_command(mname, cmdenv=cenv)
98 except Exception, e:
99 ifupdownobj.log_error(str(e))
37c0543d 100
be0b20f2 101 @classmethod
923290bd 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
105 """
a690dfae 106 # minor optimization. If operation is 'down', proceed only
107 # if interface exists in the system
923290bd 108 ifacename = ifaceobjs[0].name
109 if ('down' in ops[0] and
110 not ifupdownobj.link_exists(ifacename)):
525f0a30 111 ifupdownobj.logger.debug('%s: does not exist' %ifacename)
923290bd 112 # run posthook before you get out of here, so that
113 # appropriate cleanup is done
114 posthookfunc = ifupdownobj.sched_hooks.get('posthook')
115 if posthookfunc:
116 for ifaceobj in ifaceobjs:
117 ifaceobj.status = ifaceStatus.SUCCESS
118 posthookfunc(ifupdownobj, ifaceobj, 'down')
a690dfae 119 return
923290bd 120 for op in ops:
121 # first run ifupdownobj handlers. This is good enough
122 # for the first object in the list
123 handler = ifupdownobj.ops_handlers.get(op)
124 if handler:
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')
136 if posthookfunc:
137 posthookfunc(ifupdownobj, ifaceobj, op)
21c7daa7 138
139 @classmethod
fa3da4be 140 def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
141 followdependents=False):
99b212b0 142 """ Check if upperifaces are hanging off us and help caller decide
143 if he can proceed with the ops on this device
21c7daa7 144
99b212b0 145 Returns True or False indicating the caller to proceed with the
146 operation.
147 """
86fc62e2 148 # proceed only for down operation
149 if 'down' not in ops[0]:
150 return True
151
65c48517 152 if (ifupdownobj.FORCE or
153 not ifupdownobj.ADDONS_ENABLE or
86fc62e2 154 (not ifupdownobj.is_ifaceobj_noconfig(ifaceobj) and
155 ifupdownobj.config.get('warn_on_ifdown', '0') == '0')):
99b212b0 156 return True
65c48517 157
62ddec8b 158 ulist = ifaceobj.upperifaces
99b212b0 159 if not ulist:
160 return True
161 # Get the list of upper ifaces other than the parent
162 tmpulist = ([u for u in ulist if u != parent] if parent
163 else ulist)
164 if not tmpulist:
165 return True
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
169 for u in tmpulist:
170 if ifupdownobj.link_exists(u):
171 if not ifupdownobj.ALL:
86fc62e2 172 if ifupdownobj.is_ifaceobj_noconfig(ifaceobj):
173 ifupdownobj.logger.info('%s: skipping interface down,'
174 %ifaceobj.name + ' upperiface %s still around ' %u)
175 else:
176 ifupdownobj.logger.warn('%s: skipping interface down,'
177 %ifaceobj.name + ' upperiface %s still around ' %u)
99b212b0 178 return False
21c7daa7 179 return True
180
be0b20f2 181 @classmethod
182 def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
d08d5f54 183 order=ifaceSchedulerFlags.POSTORDER,
184 followdependents=True):
6ef5bfa2 185 """ runs interface by traversing all nodes rooted at itself """
186
d08d5f54 187 # Each ifacename can have a list of iface objects
31a5f4c3 188 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
189 if not ifaceobjs:
d08d5f54 190 raise Exception('%s: not found' %ifacename)
a6f80f0e 191
d08d5f54 192 for ifaceobj in ifaceobjs:
ca3f4fc7 193 if not cls._check_upperifaces(ifupdownobj, ifaceobj,
194 ops, parent, followdependents):
21c7daa7 195 return
f3215127 196
923290bd 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)
200
201 for ifaceobj in ifaceobjs:
f3215127 202 # Run lowerifaces or dependents
62ddec8b 203 dlist = ifaceobj.lowerifaces
f3215127 204 if dlist:
fa3da4be 205 ifupdownobj.logger.debug('%s: found dependents %s'
206 %(ifacename, str(dlist)))
d08d5f54 207 try:
208 if not followdependents:
209 # XXX: this is yet another extra step,
210 # but is needed for interfaces that are
f3215127 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
d08d5f54 214 new_dlist = [d for d in dlist
923290bd 215 if ifupdownobj.is_iface_noconfig(d)]
6ef5bfa2 216 if new_dlist:
be0b20f2 217 cls.run_iface_list(ifupdownobj, new_dlist, ops,
923290bd 218 ifacename, order, followdependents,
219 continueonfailure=False)
d08d5f54 220 else:
be0b20f2 221 cls.run_iface_list(ifupdownobj, dlist, ops,
f3215127 222 ifacename, order,
223 followdependents,
6ef5bfa2 224 continueonfailure=False)
d08d5f54 225 except Exception, e:
be0b20f2 226 if (ifupdownobj.ignore_error(str(e))):
d08d5f54 227 pass
228 else:
229 # Dont bring the iface up if children did not come up
a690dfae 230 ifaceobj.set_state_n_status(ifaceState.NEW,
923290bd 231 ifaceStatus.ERROR)
d08d5f54 232 raise
923290bd 233 if order == ifaceSchedulerFlags.POSTORDER:
234 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
a6f80f0e 235
be0b20f2 236 @classmethod
237 def run_iface_list(cls, ifupdownobj, ifacenames,
f3215127 238 ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
6ef5bfa2 239 followdependents=True, continueonfailure=True):
d08d5f54 240 """ Runs interface list """
a6f80f0e 241
d08d5f54 242 for ifacename in ifacenames:
a6f80f0e 243 try:
be0b20f2 244 cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
d08d5f54 245 order, followdependents)
a6f80f0e 246 except Exception, e:
6ef5bfa2 247 if continueonfailure:
69f58278 248 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
249 traceback.print_tb(sys.exc_info()[2])
be0b20f2 250 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
d08d5f54 251 pass
252 else:
be0b20f2 253 if (ifupdownobj.ignore_error(str(e))):
6ef5bfa2 254 pass
255 else:
9dce3561 256 raise Exception('%s : (%s)' %(ifacename, str(e)))
d08d5f54 257
be0b20f2 258 @classmethod
c798b0f4 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 """
262
263 # Each ifacename can have a list of iface objects
264 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
265 if not ifaceobjs:
266 raise Exception('%s: not found' %ifacename)
267
923290bd 268 if not skip_root:
269 # run the iface first and then its upperifaces
270 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
c798b0f4 271 for ifaceobj in ifaceobjs:
c798b0f4 272 # Run upperifaces
62ddec8b 273 ulist = ifaceobj.upperifaces
c798b0f4 274 if ulist:
fa3da4be 275 ifupdownobj.logger.debug('%s: found upperifaces %s'
276 %(ifacename, str(ulist)))
c798b0f4 277 try:
278 cls.run_iface_list_upper(ifupdownobj, ulist, ops,
279 ifacename,
280 followdependents,
281 continueonfailure=True)
282 except Exception, e:
283 if (ifupdownobj.ignore_error(str(e))):
284 pass
285 else:
286 raise
287
288 @classmethod
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 """
293
294 for ifacename in ifacenames:
295 try:
296 cls.run_iface_graph_upper(ifupdownobj, ifacename, ops, parent,
297 followdependents, skip_root)
298 except Exception, e:
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)))
303 pass
304 else:
305 if (ifupdownobj.ignore_error(str(e))):
306 pass
307 else:
9dce3561 308 raise Exception('%s : (%s)' %(ifacename, str(e)))
c798b0f4 309
310 @classmethod
311 def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
312 dependency_graph=None, indegrees=None,
d08d5f54 313 order=ifaceSchedulerFlags.POSTORDER,
314 followdependents=True):
315 """ Runs iface dependeny graph by visiting all the nodes
316
317 Parameters:
318 -----------
319 ifupdownobj : ifupdown object (used for getting and updating iface
320 object state)
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']
324
325 indegrees : indegree array if present is used to determine roots
326 of the graphs in the dependency_graph
327 """
c798b0f4 328
329 if not ifupdownobj.ALL or not followdependents or len(ifacenames) == 1:
ca3f4fc7 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
923290bd 334 if not ifupdownobj.link_exists(i)]
335 else False)
c798b0f4 336 cls.run_iface_list(ifupdownobj, ifacenames, ops,
337 parent=None,order=order,
338 followdependents=followdependents)
ca3f4fc7 339 if (not ifupdownobj.ALL and
923290bd 340 (followdependents or followupperifaces) and
341 'up' in ops[0]):
c798b0f4 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,
347 skip_root=True)
348 cls._STATE_CHECK = True
349 return
d08d5f54 350 run_queue = []
f3215127 351
c798b0f4 352 # Get a sorted list of all interfaces
20dd6242 353 if not indegrees:
f3215127 354 indegrees = OrderedDict()
d08d5f54 355 for ifacename in dependency_graph.keys():
f3215127 356 indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
f3215127 357 sorted_ifacenames = graph.topological_sort_graphs_all(dependency_graph,
f90dd038 358 indegrees)
be0b20f2 359 ifupdownobj.logger.debug('sorted ifacenames %s : '
360 %str(sorted_ifacenames))
f3215127 361
c798b0f4 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)]
d08d5f54 368
20dd6242 369 ifupdownobj.logger.debug('graph roots (interfaces that dont have '
370 'dependents):' + ' %s' %str(run_queue))
c798b0f4 371 cls.run_iface_list(ifupdownobj, run_queue, ops,
be0b20f2 372 parent=None,order=order,
373 followdependents=followdependents)