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