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