]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/ifupdown/scheduler.py
scheduler: env variable not properly set for user commands
[mirror_ifupdown2.git] / ifupdown2 / ifupdown / scheduler.py
1 #!/usr/bin/env python3
2 #
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6 # ifaceScheduler --
7 # interface scheduler
8 #
9
10 import os
11 import sys
12
13 try:
14 from ifupdown2.ifupdown.graph import *
15 from ifupdown2.ifupdown.iface import *
16 from ifupdown2.ifupdown.utils import utils
17 from ifupdown2.ifupdown.statemanager import *
18
19 import ifupdown2.ifupdown.policymanager as policymanager
20 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
21 except (ImportError, ModuleNotFoundError):
22 from ifupdown.graph import *
23 from ifupdown.iface import *
24 from ifupdown.utils import utils
25 from ifupdown.statemanager import *
26
27 import ifupdown.ifupdownflags as ifupdownflags
28 import ifupdown.policymanager as policymanager
29
30
31 class ifaceSchedulerFlags():
32 """ Enumerates scheduler flags """
33
34 INORDER = 0x1
35 POSTORDER = 0x2
36
37 class ifaceScheduler():
38 """ scheduler functions to schedule configuration of interfaces.
39
40 supports scheduling of interfaces serially in plain interface list
41 or dependency graph format.
42
43 """
44
45 _STATE_CHECK = True
46
47 _SCHED_STATUS = True
48
49 VRF_MGMT_DEVNAME = policymanager.policymanager_api.get_module_globals(
50 module_name="vrf",
51 attr="vrf-mgmt-devname"
52 )
53
54 @classmethod
55 def reset(cls):
56 cls._STATE_CHECK = True
57 cls._SCHED_STATUS = True
58
59 @classmethod
60 def get_sched_status(cls):
61 return cls._SCHED_STATUS
62
63 @classmethod
64 def set_sched_status(cls, state):
65 cls._SCHED_STATUS = state
66
67 @classmethod
68 def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv=None):
69 """ Runs sub operation on an interface """
70 ifacename = ifaceobj.name
71
72 if ifupdownobj.type and ifupdownobj.type != ifaceobj.type:
73 return
74
75 if not ifupdownobj.flags.ADDONS_ENABLE: return
76 if op == 'query-checkcurr':
77 query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj)
78 # If not type bridge vlan and the object does not exist,
79 # mark not found and return
80 if (not ifupdownobj.link_exists(ifaceobj.name) and
81 ifaceobj.type != ifaceType.BRIDGE_VLAN):
82 query_ifaceobj.set_state_n_status(ifaceState.from_str(op),
83 ifaceStatus.NOTFOUND)
84 return
85 for mname in ifupdownobj.module_ops.get(op):
86 m = ifupdownobj.modules.get(mname)
87 err = 0
88 try:
89 if hasattr(m, 'run'):
90 msg = ('%s: %s : running module %s' %(ifacename, op, mname))
91 if op == 'query-checkcurr':
92 # Dont check curr if the interface object was
93 # auto generated
94 if (ifaceobj.priv_flags and
95 ifaceobj.priv_flags.NOCONFIG):
96 continue
97 ifupdownobj.logger.debug(msg)
98 m.run(ifaceobj, op, query_ifaceobj,
99 ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
100 else:
101 ifupdownobj.logger.debug(msg)
102 m.run(ifaceobj, op,
103 ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
104 except Exception as e:
105 if not ifupdownobj.ignore_error(str(e)):
106 err = 1
107 #import traceback
108 #traceback.print_exc()
109
110 ifupdownobj.logger.error(str(e))
111 # Continue with rest of the modules
112 pass
113 finally:
114 if err or ifaceobj.status == ifaceStatus.ERROR:
115 ifaceobj.set_state_n_status(ifaceState.from_str(op),
116 ifaceStatus.ERROR)
117 if 'up' in op or 'down' in op or 'query-checkcurr' in op:
118 cls.set_sched_status(False)
119 else:
120 # Mark success only if the interface was not already
121 # marked with error
122 status = (ifaceobj.status
123 if ifaceobj.status == ifaceStatus.ERROR
124 else ifaceStatus.SUCCESS)
125 ifaceobj.set_state_n_status(ifaceState.from_str(op),
126 status)
127
128 if ifupdownobj.config.get('addon_scripts_support', '0') == '1':
129 # execute /etc/network/ scripts
130 command_env = (cenv or {}).copy()
131 command_env.update({
132 "IFACE": ifaceobj.name if ifaceobj.name else "",
133 "LOGICAL": ifaceobj.name if ifaceobj.name else "",
134 "METHOD": ifaceobj.addr_method if ifaceobj.addr_method else "",
135 "ADDRFAM": ','.join(ifaceobj.addr_family) if ifaceobj.addr_family else "",
136 })
137
138 for mname in ifupdownobj.script_ops.get(op, []):
139 ifupdownobj.logger.debug("%s: %s : running script %s" % (ifacename, op, mname))
140 try:
141 utils.exec_command(mname, env=command_env)
142 except Exception as e:
143 if "permission denied" in str(e).lower():
144 ifupdownobj.logger.warning('%s: %s %s' % (ifacename, op, str(e)))
145 else:
146 ifupdownobj.log_error('%s: %s %s' % (ifacename, op, str(e)))
147
148 @classmethod
149 def run_iface_list_ops(cls, ifupdownobj, ifaceobjs, ops):
150 """ Runs all operations on a list of interface
151 configurations for the same interface
152 """
153
154 # minor optimization. If operation is 'down', proceed only
155 # if interface exists in the system
156 ifacename = ifaceobjs[0].name
157 ifupdownobj.logger.info('%s: running ops ...' %ifacename)
158 if ('down' in ops[0] and
159 ifaceobjs[0].type != ifaceType.BRIDGE_VLAN and
160 ifaceobjs[0].addr_method != 'ppp' and
161 not ifupdownobj.link_exists(ifacename)):
162 ifupdownobj.logger.debug('%s: does not exist' %ifacename)
163 # run posthook before you get out of here, so that
164 # appropriate cleanup is done
165 posthookfunc = ifupdownobj.sched_hooks.get('posthook')
166 if posthookfunc:
167 for ifaceobj in ifaceobjs:
168 ifaceobj.status = ifaceStatus.SUCCESS
169 posthookfunc(ifupdownobj, ifaceobj, 'down')
170 return
171 for op in ops:
172 # first run ifupdownobj handlers. This is good enough
173 # for the first object in the list
174 handler = ifupdownobj.ops_handlers.get(op)
175 if handler:
176 try:
177 handler(ifupdownobj, ifaceobjs[0])
178 except Exception as e:
179 if not ifupdownobj.link_master_slave_ignore_error(str(e)):
180 ifupdownobj.logger.warning('%s: %s'
181 %(ifaceobjs[0].name, str(e)))
182 pass
183 for ifaceobj in ifaceobjs:
184 cls.run_iface_op(ifupdownobj, ifaceobj, op,
185 cenv=ifupdownobj.generate_running_env(ifaceobj, op)
186 if ifupdownobj.config.get('addon_scripts_support',
187 '0') == '1' else None)
188 posthookfunc = ifupdownobj.sched_hooks.get('posthook')
189 if posthookfunc:
190 try:
191 [posthookfunc(ifupdownobj, ifaceobj, ops[0])
192 for ifaceobj in ifaceobjs]
193 except Exception as e:
194 ifupdownobj.logger.warning('%s' %str(e))
195 pass
196
197 @classmethod
198 def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
199 followdependents=False):
200 """ Check if upperifaces are hanging off us and help caller decide
201 if he can proceed with the ops on this device
202
203 Returns True or False indicating the caller to proceed with the
204 operation.
205 """
206 # proceed only for down operation
207 if 'down' not in ops[0]:
208 return True
209
210 if (ifupdownobj.flags.SCHED_SKIP_CHECK_UPPERIFACES):
211 return True
212
213 if (ifupdownflags.flags.FORCE or
214 not ifupdownobj.flags.ADDONS_ENABLE or
215 (not ifupdownobj.is_ifaceobj_noconfig(ifaceobj) and
216 ifupdownobj.config.get('warn_on_ifdown', '0') == '0' and
217 not ifupdownflags.flags.ALL)):
218 return True
219
220 ulist = ifaceobj.upperifaces
221 if not ulist:
222 return True
223
224 # Get the list of upper ifaces other than the parent
225 tmpulist = ([u for u in ulist if u != parent] if parent
226 else ulist)
227 if not tmpulist:
228 return True
229 # XXX: This is expensive. Find a cheaper way to do this.
230 # if any of the upperdevs are present,
231 # return false to the caller to skip this interface
232 for u in tmpulist:
233 if ifupdownobj.link_exists(u):
234 if not ifupdownflags.flags.ALL:
235 if ifupdownobj.is_ifaceobj_noconfig(ifaceobj):
236 ifupdownobj.logger.info('%s: skipping interface down,'
237 %ifaceobj.name + ' upperiface %s still around ' %u)
238 else:
239 ifupdownobj.logger.warning('%s: skipping interface down,'
240 %ifaceobj.name + ' upperiface %s still around ' %u)
241 return False
242 return True
243
244 @classmethod
245 def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
246 order=ifaceSchedulerFlags.POSTORDER,
247 followdependents=True):
248 """ runs interface by traversing all nodes rooted at itself """
249
250 # Each ifacename can have a list of iface objects
251 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
252 if not ifaceobjs:
253 raise Exception('%s: not found' %ifacename)
254
255 # Check state of the dependent. If it is already brought up, return
256 if (cls._STATE_CHECK and
257 (ifaceobjs[0].state == ifaceState.from_str(ops[-1]))):
258 ifupdownobj.logger.debug('%s: already processed' %ifacename)
259 return
260
261 for ifaceobj in ifaceobjs:
262 if not cls._check_upperifaces(ifupdownobj, ifaceobj,
263 ops, parent, followdependents):
264 return
265
266 # If inorder, run the iface first and then its dependents
267 if order == ifaceSchedulerFlags.INORDER:
268 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
269
270 for ifaceobj in ifaceobjs:
271 # Run lowerifaces or dependents
272 dlist = ifaceobj.lowerifaces
273 if dlist:
274
275 if ifaceobj.link_kind == ifaceLinkKind.VRF:
276 # remove non-auto lowerifaces from 'dlist'
277 for lower_ifname in list(dlist):
278 for lower_ifaceobj in ifupdownobj.get_ifaceobjs(lower_ifname) or []:
279 if lower_ifaceobj and not lower_ifaceobj.auto and ifaceobj.name == cls.VRF_MGMT_DEVNAME:
280 dlist.remove(lower_ifname)
281
282 ifupdownobj.logger.debug('%s: found dependents %s'
283 %(ifacename, str(dlist)))
284 try:
285 if not followdependents:
286 # XXX: this is yet another extra step,
287 # but is needed for interfaces that are
288 # implicit dependents. even though we are asked to
289 # not follow dependents, we must follow the ones
290 # that dont have user given config. Because we own them
291 new_dlist = [d for d in dlist
292 if ifupdownobj.is_iface_noconfig(d)]
293 if new_dlist:
294 cls.run_iface_list(ifupdownobj, new_dlist, ops,
295 ifacename, order, followdependents,
296 continueonfailure=False)
297 else:
298 cls.run_iface_list(ifupdownobj, dlist, ops,
299 ifacename, order,
300 followdependents,
301 continueonfailure=False)
302 except Exception as e:
303 if (ifupdownobj.ignore_error(str(e))):
304 pass
305 else:
306 # Dont bring the iface up if children did not come up
307 ifaceobj.set_state_n_status(ifaceState.NEW,
308 ifaceStatus.ERROR)
309 raise
310 if order == ifaceSchedulerFlags.POSTORDER:
311 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
312
313 @classmethod
314 def run_iface_list(cls, ifupdownobj, ifacenames,
315 ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
316 followdependents=True, continueonfailure=True):
317 """ Runs interface list """
318
319 for ifacename in ifacenames:
320 try:
321 cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
322 order, followdependents)
323 except Exception as e:
324 if continueonfailure:
325 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
326 traceback.print_tb(sys.exc_info()[2])
327 ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
328 pass
329 else:
330 if (ifupdownobj.ignore_error(str(e))):
331 pass
332 else:
333 raise Exception('%s : (%s)' %(ifacename, str(e)))
334
335 @classmethod
336 def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
337 followdependents=True, skip_root=False):
338 """ runs interface by traversing all nodes rooted at itself """
339
340 # Each ifacename can have a list of iface objects
341 ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
342 if not ifaceobjs:
343 raise Exception('%s: not found' %ifacename)
344
345 if (cls._STATE_CHECK and
346 (ifaceobjs[0].state == ifaceState.from_str(ops[-1]))):
347 ifupdownobj.logger.debug('%s: already processed' %ifacename)
348 return
349
350 if not skip_root:
351 # run the iface first and then its upperifaces
352 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
353 for ifaceobj in ifaceobjs:
354 # Run upperifaces
355 ulist = ifaceobj.upperifaces
356 if ulist:
357 ifupdownobj.logger.debug('%s: found upperifaces %s'
358 %(ifacename, str(ulist)))
359 try:
360 cls.run_iface_list_upper(ifupdownobj, ulist, ops,
361 ifacename,
362 followdependents,
363 continueonfailure=True)
364 except Exception as e:
365 if (ifupdownobj.ignore_error(str(e))):
366 pass
367 else:
368 raise
369
370 @classmethod
371 def run_iface_list_upper(cls, ifupdownobj, ifacenames,
372 ops, parent=None, followdependents=True,
373 continueonfailure=True, skip_root=False):
374 """ Runs interface list """
375
376 for ifacename in ifacenames:
377 try:
378 cls.run_iface_graph_upper(ifupdownobj, ifacename, ops, parent,
379 followdependents, skip_root)
380 except Exception as e:
381 if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
382 traceback.print_tb(sys.exc_info()[2])
383 ifupdownobj.logger.warning('%s : %s' %(ifacename, str(e)))
384 pass
385
386 @classmethod
387 def _get_valid_upperifaces(cls, ifupdownobj, ifacenames,
388 allupperifacenames):
389 """ Recursively find valid upperifaces
390
391 valid upperifaces are:
392 - An upperiface which had no user config (example builtin
393 interfaces. usually vlan interfaces.)
394 - or had config and previously up
395 - and interface currently does not exist
396 - or is a bridge (because if your upperiface was a bridge
397 - u will have to execute up on the bridge
398 to enslave the port and apply bridge attributes to the port) """
399
400 upperifacenames = []
401 for ifacename in ifacenames:
402 # get upperifaces
403 ifaceobj = ifupdownobj.get_ifaceobj_first(ifacename)
404 if not ifaceobj:
405 continue
406 ulist = set(ifaceobj.upperifaces or []).difference(upperifacenames)
407 nulist = []
408 for u in ulist:
409 uifaceobj = ifupdownobj.get_ifaceobj_first(u)
410 if not uifaceobj:
411 continue
412 has_config = not (uifaceobj.priv_flags and
413 uifaceobj.priv_flags.NOCONFIG)
414 if (((has_config and ifupdownobj.get_ifaceobjs_saved(u)) or
415 not has_config) and (not ifupdownobj.link_exists(u)
416 # Do this always for a bridge. Note that this is
417 # not done for a vlan aware bridge because,
418 # in the vlan aware bridge case, the bridge module
419 # applies the bridge port configuration on the port
420 # when up is scheduled on the port.
421 or (uifaceobj.link_kind == ifaceLinkKind.BRIDGE))):
422 nulist.append(u)
423 upperifacenames.extend(nulist)
424 allupperifacenames.extend(upperifacenames)
425 if upperifacenames:
426 cls._get_valid_upperifaces(ifupdownobj, upperifacenames,
427 allupperifacenames)
428 return
429
430 @classmethod
431 def run_upperifaces(cls, ifupdownobj, ifacenames, ops,
432 continueonfailure=True):
433 """ Run through valid upperifaces """
434 upperifaces = []
435
436 cls._get_valid_upperifaces(ifupdownobj, ifacenames, upperifaces)
437 if not upperifaces:
438 return
439 # dump valid upperifaces
440 ifupdownobj.logger.debug(upperifaces)
441 for u in upperifaces:
442 try:
443 ifaceobjs = ifupdownobj.get_ifaceobjs(u)
444 if not ifaceobjs:
445 continue
446 cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
447 except Exception as e:
448 if continueonfailure:
449 ifupdownobj.logger.warning('%s' %str(e))
450
451 @classmethod
452 def _dump_dependency_info(cls, ifupdownobj, ifacenames,
453 dependency_graph=None, indegrees=None):
454 ifupdownobj.logger.info('{\n')
455 ifupdownobj.logger.info('\nifaceobjs:')
456 for iname in ifacenames:
457 iobjs = ifupdownobj.get_ifaceobjs(iname)
458 for iobj in iobjs:
459 iobj.dump(ifupdownobj.logger)
460 if (dependency_graph):
461 ifupdownobj.logger.info('\nDependency Graph:')
462 ifupdownobj.logger.info(dependency_graph)
463 if (indegrees):
464 ifupdownobj.logger.info('\nIndegrees:')
465 ifupdownobj.logger.info(indegrees)
466 ifupdownobj.logger.info('}\n')
467
468 @classmethod
469 def get_sorted_iface_list(cls, ifupdownobj, ifacenames, ops,
470 dependency_graph, indegrees=None):
471 if len(ifacenames) == 1:
472 return ifacenames
473 # Get a sorted list of all interfaces
474 if not indegrees:
475 indegrees = OrderedDict()
476 for ifacename in list(dependency_graph.keys()):
477 indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
478
479 #cls._dump_dependency_info(ifupdownobj, ifacenames,
480 # dependency_graph, indegrees)
481
482 ifacenames_all_sorted = graph.topological_sort_graphs_all(
483 dependency_graph, indegrees)
484 # if ALL was set, return all interfaces
485 if ifupdownflags.flags.ALL:
486 return ifacenames_all_sorted
487
488 # else return ifacenames passed as argument in sorted order
489 ifacenames_sorted = []
490 [ifacenames_sorted.append(ifacename)
491 for ifacename in ifacenames_all_sorted
492 if ifacename in ifacenames]
493 return ifacenames_sorted
494
495 @classmethod
496 def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
497 dependency_graph=None, indegrees=None,
498 order=ifaceSchedulerFlags.POSTORDER,
499 followdependents=True, skipupperifaces=False, sort=False):
500 """ runs interface configuration modules on interfaces passed as
501 argument. Runs topological sort on interface dependency graph.
502
503 Args:
504 **ifupdownobj** (object): ifupdownMain object
505
506 **ifacenames** (list): list of interface names
507
508 **ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
509
510 **dependency_graph** (dict): dependency graph in adjacency list format
511
512 Kwargs:
513 **indegrees** (dict): indegree array of the dependency graph
514
515 **order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
516
517 **followdependents** (bool): follow dependent interfaces if true
518
519 **sort** (bool): sort ifacelist in the case where ALL is not set
520
521 """
522 #
523 # Algo:
524 # if ALL/auto interfaces are specified,
525 # - walk the dependency tree in postorder or inorder depending
526 # on the operation.
527 # (This is to run interfaces correctly in order)
528 # else:
529 # - sort iface list if the ifaces belong to a "class"
530 # - else just run iface list in the order they were specified
531 #
532 # Run any upperifaces if available
533 #
534 followupperifaces = False
535 run_queue = []
536 skip_ifacesort = int(ifupdownobj.config.get('skip_ifacesort', '0'))
537 if not skip_ifacesort and not indegrees:
538 indegrees = OrderedDict()
539 for ifacename in list(dependency_graph.keys()):
540 indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
541
542 if not ifupdownflags.flags.ALL:
543 if 'up' in ops[0]:
544 # If there is any interface that does not exist, maybe it
545 # is a logical interface and we have to followupperifaces
546 # when it comes up, so lets get that list.
547 if any([True for i in ifacenames
548 if ifupdownobj.must_follow_upperifaces(i)]):
549 followupperifaces = (True if
550 [i for i in ifacenames
551 if not ifupdownobj.link_exists(i)]
552 else False)
553 # sort interfaces only if the caller asked to sort
554 # and skip_ifacesort is not on.
555 if not skip_ifacesort and sort:
556 run_queue = cls.get_sorted_iface_list(ifupdownobj, ifacenames,
557 ops, dependency_graph, indegrees)
558 if run_queue and 'up' in ops[0]:
559 run_queue.reverse()
560 else:
561 # if -a is set, we pick the interfaces
562 # that have no parents and use a sorted list of those
563 if not skip_ifacesort:
564 sorted_ifacenames = cls.get_sorted_iface_list(ifupdownobj,
565 ifacenames, ops, dependency_graph,
566 indegrees)
567 if sorted_ifacenames:
568 # pick interfaces that user asked
569 # and those that dont have any dependents first
570 [run_queue.append(ifacename)
571 for ifacename in sorted_ifacenames
572 if ifacename in ifacenames and
573 not indegrees.get(ifacename)]
574 ifupdownobj.logger.debug('graph roots (interfaces that ' +
575 'dont have dependents):' + ' %s' %str(run_queue))
576 else:
577 ifupdownobj.logger.warning('interface sort returned None')
578
579 # If queue not present, just run interfaces that were asked by the
580 # user
581 if not run_queue:
582 run_queue = list(ifacenames)
583 # if we are taking the order of interfaces as specified
584 # in the interfaces file, we should reverse the list if we
585 # want to down. This can happen if 'skip_ifacesort'
586 # is been specified.
587 if 'down' in ops[0]:
588 run_queue.reverse()
589
590 # run interface list
591 cls.run_iface_list(ifupdownobj, run_queue, ops,
592 parent=None, order=order,
593 followdependents=followdependents)
594 if not cls.get_sched_status():
595 return
596
597 if (not skipupperifaces and
598 ifupdownobj.config.get('skip_upperifaces', '0') == '0' and
599 ((not ifupdownflags.flags.ALL and followdependents) or
600 followupperifaces) and
601 'up' in ops[0]):
602 # If user had given a set of interfaces to bring up
603 # try and execute 'up' on the upperifaces
604 ifupdownobj.logger.info('running upperifaces (parent interfaces) ' +
605 'if available ..')
606 try:
607 # upperiface bring up is best effort.
608 # eg case: if we are bringing up a bridge port
609 # this section does an 'ifup on the bridge'
610 # so that the recently up'ed bridge port gets enslaved
611 # to the bridge. But the up on the bridge may
612 # throw out more errors if the bridge is not
613 # in the correct state. Lets not surprise
614 # the user with such errors when he has
615 # only requested to bring up the bridge port.
616 cls._STATE_CHECK = False
617 ifupdownflags.flags.IGNORE_ERRORS = True
618 cls.run_upperifaces(ifupdownobj, ifacenames, ops)
619 finally:
620 ifupdownflags.flags.IGNORE_ERRORS = False
621 cls._STATE_CHECK = True
622 # upperiface bringup is best effort, so dont propagate errors
623 # reset scheduler status to True
624 cls.set_sched_status(True)