]> git.proxmox.com Git - mirror_ifupdown2.git/blame - pkg/scheduler.py
cleanup + fixes
[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
121 Returns False if this interface needs to be skipped, else return True """
122
c798b0f4 123 if 'up' in ops[0] and followdependents:
124 return True
125
21c7daa7 126 # Deal with upperdevs first
62ddec8b 127 ulist = ifaceobj.upperifaces
21c7daa7 128 if ulist:
129 tmpulist = ([u for u in ulist if u != parent] if parent
130 else ulist)
131 if not tmpulist:
21c7daa7 132 return True
133 if 'down' in ops[0]:
134 # XXX: This is expensive. Find a cheaper way to do this
135 # if any of the upperdevs are present,
136 # dont down this interface
137 for u in tmpulist:
138 if ifupdownobj.link_exists(u):
139 if not ifupdownobj.FORCE and not ifupdownobj.ALL:
fa3da4be 140 ifupdownobj.logger.warn('%s: ' %ifaceobj.name +
141 'upperiface %s still around' %u)
142 return True
21c7daa7 143 elif 'up' in ops[0] and not ifupdownobj.ALL:
144 # For 'up', just warn that there is an upperdev which is
145 # probably not up
146 for u in tmpulist:
147 if not ifupdownobj.link_exists(u):
fa3da4be 148 ifupdownobj.logger.warn('%s: ' %ifaceobj.name +
149 'upper iface %s does not exist' %u)
21c7daa7 150 return True
151
be0b20f2 152 @classmethod
153 def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
d08d5f54 154 order=ifaceSchedulerFlags.POSTORDER,
155 followdependents=True):
6ef5bfa2 156 """ runs interface by traversing all nodes rooted at itself """
157
d08d5f54 158 # Each ifacename can have a list of iface objects
31a5f4c3 159 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
160 if not ifaceobjs:
d08d5f54 161 raise Exception('%s: not found' %ifacename)
a6f80f0e 162
d08d5f54 163 for ifaceobj in ifaceobjs:
c798b0f4 164 if not cls._check_upperifaces(ifupdownobj, ifaceobj, ops, parent,
165 followdependents):
21c7daa7 166 return
d08d5f54 167 if order == ifaceSchedulerFlags.INORDER:
f3215127 168 # If inorder, run the iface first and then its dependents
be0b20f2 169 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
f3215127 170
171 # Run lowerifaces or dependents
62ddec8b 172 dlist = ifaceobj.lowerifaces
f3215127 173 if dlist:
fa3da4be 174 ifupdownobj.logger.debug('%s: found dependents %s'
175 %(ifacename, str(dlist)))
d08d5f54 176 try:
177 if not followdependents:
178 # XXX: this is yet another extra step,
179 # but is needed for interfaces that are
f3215127 180 # implicit dependents. even though we are asked to
181 # not follow dependents, we must follow the ones
182 # that dont have user given config. Because we own them
d08d5f54 183 new_dlist = [d for d in dlist
184 if ifupdownobj.is_iface_noconfig(d)]
6ef5bfa2 185 if new_dlist:
be0b20f2 186 cls.run_iface_list(ifupdownobj, new_dlist, ops,
f3215127 187 ifacename, order,
188 followdependents,
6ef5bfa2 189 continueonfailure=False)
d08d5f54 190 else:
be0b20f2 191 cls.run_iface_list(ifupdownobj, dlist, ops,
f3215127 192 ifacename, order,
193 followdependents,
6ef5bfa2 194 continueonfailure=False)
d08d5f54 195 except Exception, e:
be0b20f2 196 if (ifupdownobj.ignore_error(str(e))):
d08d5f54 197 pass
198 else:
199 # Dont bring the iface up if children did not come up
a690dfae 200 ifaceobj.set_state_n_status(ifaceState.NEW,
201 ifaceStatus.ERROR)
d08d5f54 202 raise
d08d5f54 203 if order == ifaceSchedulerFlags.POSTORDER:
be0b20f2 204 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
a6f80f0e 205
be0b20f2 206 @classmethod
207 def run_iface_list(cls, ifupdownobj, ifacenames,
f3215127 208 ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
6ef5bfa2 209 followdependents=True, continueonfailure=True):
d08d5f54 210 """ Runs interface list """
a6f80f0e 211
d08d5f54 212 for ifacename in ifacenames:
a6f80f0e 213 try:
be0b20f2 214 cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
d08d5f54 215 order, followdependents)
a6f80f0e 216 except Exception, e:
6ef5bfa2 217 if continueonfailure:
69f58278 218 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
219 traceback.print_tb(sys.exc_info()[2])
be0b20f2 220 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
d08d5f54 221 pass
222 else:
be0b20f2 223 if (ifupdownobj.ignore_error(str(e))):
6ef5bfa2 224 pass
225 else:
226 raise Exception('error running iface %s (%s)'
227 %(ifacename, str(e)))
d08d5f54 228
be0b20f2 229 @classmethod
c798b0f4 230 def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
231 followdependents=True, skip_root=False):
232 """ runs interface by traversing all nodes rooted at itself """
233
234 # Each ifacename can have a list of iface objects
235 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
236 if not ifaceobjs:
237 raise Exception('%s: not found' %ifacename)
238
239 for ifaceobj in ifaceobjs:
240 if not skip_root:
241 # run the iface first and then its upperifaces
242 cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
243
244 # Run upperifaces
62ddec8b 245 ulist = ifaceobj.upperifaces
c798b0f4 246 if ulist:
fa3da4be 247 ifupdownobj.logger.debug('%s: found upperifaces %s'
248 %(ifacename, str(ulist)))
c798b0f4 249 try:
250 cls.run_iface_list_upper(ifupdownobj, ulist, ops,
251 ifacename,
252 followdependents,
253 continueonfailure=True)
254 except Exception, e:
255 if (ifupdownobj.ignore_error(str(e))):
256 pass
257 else:
258 raise
259
260 @classmethod
261 def run_iface_list_upper(cls, ifupdownobj, ifacenames,
262 ops, parent=None, followdependents=True,
263 continueonfailure=True, skip_root=False):
264 """ Runs interface list """
265
266 for ifacename in ifacenames:
267 try:
268 cls.run_iface_graph_upper(ifupdownobj, ifacename, ops, parent,
269 followdependents, skip_root)
270 except Exception, e:
271 if continueonfailure:
272 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
273 traceback.print_tb(sys.exc_info()[2])
274 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
275 pass
276 else:
277 if (ifupdownobj.ignore_error(str(e))):
278 pass
279 else:
280 raise Exception('error running iface %s (%s)'
281 %(ifacename, str(e)))
282
283 @classmethod
284 def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
285 dependency_graph=None, indegrees=None,
d08d5f54 286 order=ifaceSchedulerFlags.POSTORDER,
287 followdependents=True):
288 """ Runs iface dependeny graph by visiting all the nodes
289
290 Parameters:
291 -----------
292 ifupdownobj : ifupdown object (used for getting and updating iface
293 object state)
294 dependency_graph : dependency graph in adjacency list
295 format (contains more than one dependency graph)
296 ops : list of operations to perform eg ['pre-up', 'up', 'post-up']
297
298 indegrees : indegree array if present is used to determine roots
299 of the graphs in the dependency_graph
300 """
c798b0f4 301
302 if not ifupdownobj.ALL or not followdependents or len(ifacenames) == 1:
303 cls.run_iface_list(ifupdownobj, ifacenames, ops,
304 parent=None,order=order,
305 followdependents=followdependents)
306 if not ifupdownobj.ALL and followdependents and 'up' in ops[0]:
307 # If user had given a set of interfaces to bring up
308 # try and execute 'up' on the upperifaces
309 ifupdownobj.logger.info('running upperifaces if available')
310 cls._STATE_CHECK = False
311 cls.run_iface_list_upper(ifupdownobj, ifacenames, ops,
312 skip_root=True)
313 cls._STATE_CHECK = True
314 return
d08d5f54 315 run_queue = []
f3215127 316
c798b0f4 317 # Get a sorted list of all interfaces
20dd6242 318 if not indegrees:
f3215127 319 indegrees = OrderedDict()
d08d5f54 320 for ifacename in dependency_graph.keys():
f3215127 321 indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
f3215127 322 sorted_ifacenames = graph.topological_sort_graphs_all(dependency_graph,
323 dict(indegrees))
be0b20f2 324 ifupdownobj.logger.debug('sorted ifacenames %s : '
325 %str(sorted_ifacenames))
f3215127 326
c798b0f4 327 # From the sorted list, pick interfaces that user asked
328 # and those that dont have any dependents first
329 [run_queue.append(ifacename)
330 for ifacename in sorted_ifacenames
331 if ifacename in ifacenames and
332 not indegrees.get(ifacename)]
d08d5f54 333
20dd6242 334 ifupdownobj.logger.debug('graph roots (interfaces that dont have '
335 'dependents):' + ' %s' %str(run_queue))
c798b0f4 336 cls.run_iface_list(ifupdownobj, run_queue, ops,
be0b20f2 337 parent=None,order=order,
338 followdependents=followdependents)