]> git.proxmox.com Git - mirror_ifupdown2.git/blame - pkg/scheduler.py
Update/add copyright files for Cumulus .debs.
[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
52 def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv):
a6f80f0e 53 """ Runs sub operation on an interface """
62ddec8b 54 ifacename = ifaceobj.name
a6f80f0e 55
c798b0f4 56 if (cls._STATE_CHECK and
62ddec8b 57 (ifaceobj.state >= ifaceState.from_str(op)) and
58 (ifaceobj.status == ifaceStatus.SUCCESS)):
be0b20f2 59 ifupdownobj.logger.debug('%s: already in state %s' %(ifacename, op))
d08d5f54 60 return
a6f80f0e 61
be0b20f2 62 # first run ifupdownobj handlers
63 handler = ifupdownobj.ops_handlers.get(op)
64 if handler:
62ddec8b 65 if not ifaceobj.addr_method or (ifaceobj.addr_method and
66 ifaceobj.addr_method != 'manual'):
be0b20f2 67 handler(ifupdownobj, ifaceobj)
68
20dd6242 69 if not ifupdownobj.ADDONS_ENABLE: return
70
be0b20f2 71 for mname in ifupdownobj.module_ops.get(op):
37c0543d 72 m = ifupdownobj.modules.get(mname)
a6f80f0e 73 err = 0
74 try:
d08d5f54 75 if hasattr(m, 'run'):
a690dfae 76 msg = ('%s: %s : running module %s' %(ifacename, op, mname))
739f665b 77 if op == 'query-checkcurr':
d08d5f54 78 # Dont check curr if the interface object was
37c0543d 79 # auto generated
d08d5f54 80 if (ifaceobj.priv_flags & ifupdownobj.NOCONFIG):
37c0543d 81 continue
a690dfae 82 ifupdownobj.logger.debug(msg)
d08d5f54 83 m.run(ifaceobj, op,
84 query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj))
a6f80f0e 85 else:
a690dfae 86 ifupdownobj.logger.debug(msg)
d08d5f54 87 m.run(ifaceobj, op)
a6f80f0e 88 except Exception, e:
89 err = 1
be0b20f2 90 ifupdownobj.log_error(str(e))
a6f80f0e 91 finally:
31a5f4c3 92 if err:
93 ifaceobj.set_state_n_status(ifaceState.from_str(op),
94 ifaceStatus.ERROR)
d08d5f54 95 else:
31a5f4c3 96 ifaceobj.set_state_n_status(ifaceState.from_str(op),
97 ifaceStatus.SUCCESS)
6bd7fc74 98
99 if ifupdownobj.COMPAT_EXEC_SCRIPTS:
100 # execute /etc/network/ scripts
101 for mname in ifupdownobj.script_ops.get(op, []):
102 ifupdownobj.logger.debug('%s: %s : running script %s'
d08d5f54 103 %(ifacename, op, mname))
6bd7fc74 104 try:
105 ifupdownobj.exec_command(mname, cmdenv=cenv)
106 except Exception, e:
107 ifupdownobj.log_error(str(e))
37c0543d 108
be0b20f2 109 @classmethod
110 def run_iface_ops(cls, ifupdownobj, ifaceobj, ops):
31a5f4c3 111 """ Runs all operations on an interface """
62ddec8b 112 ifacename = ifaceobj.name
a690dfae 113 # minor optimization. If operation is 'down', proceed only
114 # if interface exists in the system
115 if 'down' in ops[0] and not ifupdownobj.link_exists(ifacename):
525f0a30 116 ifupdownobj.logger.debug('%s: does not exist' %ifacename)
a690dfae 117 return
6bd7fc74 118 cenv=None
6bd7fc74 119 if ifupdownobj.COMPAT_EXEC_SCRIPTS:
120 # For backward compatibility generate env variables
121 # for attributes
122 cenv = ifupdownobj.generate_running_env(ifaceobj, ops[0])
6bd7fc74 123 map(lambda op: cls.run_iface_op(ifupdownobj, ifaceobj, op, cenv), ops)
31a5f4c3 124 posthookfunc = ifupdownobj.sched_hooks.get('posthook')
125 if posthookfunc:
cb7cc592 126 posthookfunc(ifupdownobj, ifaceobj, ops[0])
21c7daa7 127
128 @classmethod
fa3da4be 129 def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
130 followdependents=False):
99b212b0 131 """ Check if upperifaces are hanging off us and help caller decide
132 if he can proceed with the ops on this device
21c7daa7 133
99b212b0 134 Returns True or False indicating the caller to proceed with the
135 operation.
136 """
86fc62e2 137 # proceed only for down operation
138 if 'down' not in ops[0]:
139 return True
140
65c48517 141 if (ifupdownobj.FORCE or
142 not ifupdownobj.ADDONS_ENABLE or
86fc62e2 143 (not ifupdownobj.is_ifaceobj_noconfig(ifaceobj) and
144 ifupdownobj.config.get('warn_on_ifdown', '0') == '0')):
99b212b0 145 return True
65c48517 146
62ddec8b 147 ulist = ifaceobj.upperifaces
99b212b0 148 if not ulist:
149 return True
150 # Get the list of upper ifaces other than the parent
151 tmpulist = ([u for u in ulist if u != parent] if parent
152 else ulist)
153 if not tmpulist:
154 return True
155 # XXX: This is expensive. Find a cheaper way to do this.
156 # if any of the upperdevs are present,
157 # return false to the caller to skip this interface
158 for u in tmpulist:
159 if ifupdownobj.link_exists(u):
160 if not ifupdownobj.ALL:
86fc62e2 161 if ifupdownobj.is_ifaceobj_noconfig(ifaceobj):
162 ifupdownobj.logger.info('%s: skipping interface down,'
163 %ifaceobj.name + ' upperiface %s still around ' %u)
164 else:
165 ifupdownobj.logger.warn('%s: skipping interface down,'
166 %ifaceobj.name + ' upperiface %s still around ' %u)
99b212b0 167 return False
21c7daa7 168 return True
169
be0b20f2 170 @classmethod
171 def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
d08d5f54 172 order=ifaceSchedulerFlags.POSTORDER,
173 followdependents=True):
6ef5bfa2 174 """ runs interface by traversing all nodes rooted at itself """
175
d08d5f54 176 # Each ifacename can have a list of iface objects
31a5f4c3 177 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
178 if not ifaceobjs:
d08d5f54 179 raise Exception('%s: not found' %ifacename)
a6f80f0e 180
d08d5f54 181 for ifaceobj in ifaceobjs:
ca3f4fc7 182 if not cls._check_upperifaces(ifupdownobj, ifaceobj,
183 ops, parent, followdependents):
21c7daa7 184 return
d08d5f54 185 if order == ifaceSchedulerFlags.INORDER:
f3215127 186 # If inorder, run the iface first and then its dependents
be0b20f2 187 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
f3215127 188
189 # Run lowerifaces or dependents
62ddec8b 190 dlist = ifaceobj.lowerifaces
f3215127 191 if dlist:
fa3da4be 192 ifupdownobj.logger.debug('%s: found dependents %s'
193 %(ifacename, str(dlist)))
d08d5f54 194 try:
195 if not followdependents:
196 # XXX: this is yet another extra step,
197 # but is needed for interfaces that are
f3215127 198 # implicit dependents. even though we are asked to
199 # not follow dependents, we must follow the ones
200 # that dont have user given config. Because we own them
d08d5f54 201 new_dlist = [d for d in dlist
202 if ifupdownobj.is_iface_noconfig(d)]
6ef5bfa2 203 if new_dlist:
be0b20f2 204 cls.run_iface_list(ifupdownobj, new_dlist, ops,
f3215127 205 ifacename, order,
206 followdependents,
6ef5bfa2 207 continueonfailure=False)
d08d5f54 208 else:
be0b20f2 209 cls.run_iface_list(ifupdownobj, dlist, ops,
f3215127 210 ifacename, order,
211 followdependents,
6ef5bfa2 212 continueonfailure=False)
d08d5f54 213 except Exception, e:
be0b20f2 214 if (ifupdownobj.ignore_error(str(e))):
d08d5f54 215 pass
216 else:
217 # Dont bring the iface up if children did not come up
a690dfae 218 ifaceobj.set_state_n_status(ifaceState.NEW,
219 ifaceStatus.ERROR)
d08d5f54 220 raise
d08d5f54 221 if order == ifaceSchedulerFlags.POSTORDER:
be0b20f2 222 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
a6f80f0e 223
be0b20f2 224 @classmethod
225 def run_iface_list(cls, ifupdownobj, ifacenames,
f3215127 226 ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
6ef5bfa2 227 followdependents=True, continueonfailure=True):
d08d5f54 228 """ Runs interface list """
a6f80f0e 229
d08d5f54 230 for ifacename in ifacenames:
a6f80f0e 231 try:
be0b20f2 232 cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
d08d5f54 233 order, followdependents)
a6f80f0e 234 except Exception, e:
6ef5bfa2 235 if continueonfailure:
69f58278 236 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
237 traceback.print_tb(sys.exc_info()[2])
be0b20f2 238 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
d08d5f54 239 pass
240 else:
be0b20f2 241 if (ifupdownobj.ignore_error(str(e))):
6ef5bfa2 242 pass
243 else:
9dce3561 244 raise Exception('%s : (%s)' %(ifacename, str(e)))
d08d5f54 245
be0b20f2 246 @classmethod
c798b0f4 247 def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
248 followdependents=True, skip_root=False):
249 """ runs interface by traversing all nodes rooted at itself """
250
251 # Each ifacename can have a list of iface objects
252 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
253 if not ifaceobjs:
254 raise Exception('%s: not found' %ifacename)
255
256 for ifaceobj in ifaceobjs:
257 if not skip_root:
258 # run the iface first and then its upperifaces
259 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
260
261 # Run upperifaces
62ddec8b 262 ulist = ifaceobj.upperifaces
c798b0f4 263 if ulist:
fa3da4be 264 ifupdownobj.logger.debug('%s: found upperifaces %s'
265 %(ifacename, str(ulist)))
c798b0f4 266 try:
267 cls.run_iface_list_upper(ifupdownobj, ulist, ops,
268 ifacename,
269 followdependents,
270 continueonfailure=True)
271 except Exception, e:
272 if (ifupdownobj.ignore_error(str(e))):
273 pass
274 else:
275 raise
276
277 @classmethod
278 def run_iface_list_upper(cls, ifupdownobj, ifacenames,
279 ops, parent=None, followdependents=True,
280 continueonfailure=True, skip_root=False):
281 """ Runs interface list """
282
283 for ifacename in ifacenames:
284 try:
285 cls.run_iface_graph_upper(ifupdownobj, ifacename, ops, parent,
286 followdependents, skip_root)
287 except Exception, e:
288 if continueonfailure:
289 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
290 traceback.print_tb(sys.exc_info()[2])
291 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
292 pass
293 else:
294 if (ifupdownobj.ignore_error(str(e))):
295 pass
296 else:
9dce3561 297 raise Exception('%s : (%s)' %(ifacename, str(e)))
c798b0f4 298
299 @classmethod
300 def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
301 dependency_graph=None, indegrees=None,
d08d5f54 302 order=ifaceSchedulerFlags.POSTORDER,
303 followdependents=True):
304 """ Runs iface dependeny graph by visiting all the nodes
305
306 Parameters:
307 -----------
308 ifupdownobj : ifupdown object (used for getting and updating iface
309 object state)
310 dependency_graph : dependency graph in adjacency list
311 format (contains more than one dependency graph)
312 ops : list of operations to perform eg ['pre-up', 'up', 'post-up']
313
314 indegrees : indegree array if present is used to determine roots
315 of the graphs in the dependency_graph
316 """
c798b0f4 317
318 if not ifupdownobj.ALL or not followdependents or len(ifacenames) == 1:
ca3f4fc7 319 # If there is any interface that does exist, maybe it is a
320 # logical interface and we have to followupperifaces
321 followupperifaces = (True if
322 [i for i in ifacenames
323 if not ifupdownobj.link_exists(i)]
324 else False)
c798b0f4 325 cls.run_iface_list(ifupdownobj, ifacenames, ops,
326 parent=None,order=order,
327 followdependents=followdependents)
ca3f4fc7 328 if (not ifupdownobj.ALL and
329 (followdependents or followupperifaces) and 'up' in ops[0]):
c798b0f4 330 # If user had given a set of interfaces to bring up
331 # try and execute 'up' on the upperifaces
332 ifupdownobj.logger.info('running upperifaces if available')
333 cls._STATE_CHECK = False
334 cls.run_iface_list_upper(ifupdownobj, ifacenames, ops,
335 skip_root=True)
336 cls._STATE_CHECK = True
337 return
d08d5f54 338 run_queue = []
f3215127 339
c798b0f4 340 # Get a sorted list of all interfaces
20dd6242 341 if not indegrees:
f3215127 342 indegrees = OrderedDict()
d08d5f54 343 for ifacename in dependency_graph.keys():
f3215127 344 indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
f3215127 345 sorted_ifacenames = graph.topological_sort_graphs_all(dependency_graph,
f90dd038 346 indegrees)
be0b20f2 347 ifupdownobj.logger.debug('sorted ifacenames %s : '
348 %str(sorted_ifacenames))
f3215127 349
c798b0f4 350 # From the sorted list, pick interfaces that user asked
351 # and those that dont have any dependents first
352 [run_queue.append(ifacename)
353 for ifacename in sorted_ifacenames
354 if ifacename in ifacenames and
355 not indegrees.get(ifacename)]
d08d5f54 356
20dd6242 357 ifupdownobj.logger.debug('graph roots (interfaces that dont have '
358 'dependents):' + ' %s' %str(run_queue))
c798b0f4 359 cls.run_iface_list(ifupdownobj, run_queue, ops,
be0b20f2 360 parent=None,order=order,
361 followdependents=followdependents)