1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00
Files
CumulusNetworks-ifupdown2/pkg/scheduler.py
roopa 99b212b016 Bring back upperiface check during down (One of my recent checkins had
removed it)

Ticket: CM-2671
Reviewed By:
Testing Done: Tested test case in CM-2671 and ifupdown2 sanity
2014-04-29 21:45:57 -07:00

354 lines
15 KiB
Python

#!/usr/bin/python
#
# Copyright 2013. Cumulus Networks, Inc.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
# ifaceScheduler --
# interface scheduler
#
from statemanager import *
from iface import *
from graph import *
from collections import deque
from collections import OrderedDict
import logging
import traceback
import sys
from graph import *
from collections import deque
from threading import *
from ifupdownbase import *
class ifaceSchedulerFlags():
INORDER = 0x1
POSTORDER = 0x2
class ifaceScheduler():
""" scheduler functions to schedule configuration of interfaces.
supports scheduling of interfaces serially in plain interface list
or dependency graph format.
Algo:
- run topological sort on the iface objects
- In the sorted iface object list, pick up interfaces with no parents
and run ops on them and their children.
- If operation is up and user gave interface list (ie not all)
option, also see if there were upper-devices and run ops on them.
- if operation is down, dont down the interface if it still
has upperifaces present. The down operation is executed when the
last upperiface goes away. If force option is set, this rule does not
apply.
- run ops calls addon modules run operation passing the iface
object and op to each module.
- ops are [pre-up, up, post-up, pre-down, down,
post-down, query-running, query-check]
"""
_STATE_CHECK = True
@classmethod
def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv):
""" Runs sub operation on an interface """
ifacename = ifaceobj.name
if (cls._STATE_CHECK and
(ifaceobj.state >= ifaceState.from_str(op)) and
(ifaceobj.status == ifaceStatus.SUCCESS)):
ifupdownobj.logger.debug('%s: already in state %s' %(ifacename, op))
return
# first run ifupdownobj handlers
handler = ifupdownobj.ops_handlers.get(op)
if handler:
if not ifaceobj.addr_method or (ifaceobj.addr_method and
ifaceobj.addr_method != 'manual'):
handler(ifupdownobj, ifaceobj)
if not ifupdownobj.ADDONS_ENABLE: return
for mname in ifupdownobj.module_ops.get(op):
m = ifupdownobj.modules.get(mname)
err = 0
try:
if hasattr(m, 'run'):
msg = ('%s: %s : running module %s' %(ifacename, op, mname))
if op == 'query-checkcurr':
# Dont check curr if the interface object was
# auto generated
if (ifaceobj.priv_flags & ifupdownobj.NOCONFIG):
continue
ifupdownobj.logger.debug(msg)
m.run(ifaceobj, op,
query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj))
else:
ifupdownobj.logger.debug(msg)
m.run(ifaceobj, op)
except Exception, e:
err = 1
ifupdownobj.log_error(str(e))
finally:
if err:
ifaceobj.set_state_n_status(ifaceState.from_str(op),
ifaceStatus.ERROR)
else:
ifaceobj.set_state_n_status(ifaceState.from_str(op),
ifaceStatus.SUCCESS)
if ifupdownobj.COMPAT_EXEC_SCRIPTS:
# execute /etc/network/ scripts
for mname in ifupdownobj.script_ops.get(op, []):
ifupdownobj.logger.debug('%s: %s : running script %s'
%(ifacename, op, mname))
try:
ifupdownobj.exec_command(mname, cmdenv=cenv)
except Exception, e:
ifupdownobj.log_error(str(e))
@classmethod
def run_iface_ops(cls, ifupdownobj, ifaceobj, ops):
""" Runs all operations on an interface """
ifacename = ifaceobj.name
# minor optimization. If operation is 'down', proceed only
# if interface exists in the system
if 'down' in ops[0] and not ifupdownobj.link_exists(ifacename):
ifupdownobj.logger.debug('%s: does not exist' %ifacename)
return
cenv=None
if ifupdownobj.COMPAT_EXEC_SCRIPTS:
# For backward compatibility generate env variables
# for attributes
cenv = ifupdownobj.generate_running_env(ifaceobj, ops[0])
map(lambda op: cls.run_iface_op(ifupdownobj, ifaceobj, op, cenv), ops)
posthookfunc = ifupdownobj.sched_hooks.get('posthook')
if posthookfunc:
posthookfunc(ifupdownobj, ifaceobj, ops[0])
@classmethod
def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
followdependents=False):
""" Check if upperifaces are hanging off us and help caller decide
if he can proceed with the ops on this device
Returns True or False indicating the caller to proceed with the
operation.
"""
if ifupdownobj.FORCE:
return True
# proceed only for down operation
if 'down' not in ops[0]:
return True
ulist = ifaceobj.upperifaces
if not ulist:
return True
# Get the list of upper ifaces other than the parent
tmpulist = ([u for u in ulist if u != parent] if parent
else ulist)
if not tmpulist:
return True
# XXX: This is expensive. Find a cheaper way to do this.
# if any of the upperdevs are present,
# return false to the caller to skip this interface
for u in tmpulist:
if ifupdownobj.link_exists(u):
if not ifupdownobj.ALL:
ifupdownobj.logger.info('%s: skip interface down,'
%ifaceobj.name + ' upperiface %s still around ' %u +
'(use --force to override)')
return False
return True
@classmethod
def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
order=ifaceSchedulerFlags.POSTORDER,
followdependents=True):
""" runs interface by traversing all nodes rooted at itself """
# Each ifacename can have a list of iface objects
ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
if not ifaceobjs:
raise Exception('%s: not found' %ifacename)
for ifaceobj in ifaceobjs:
if not cls._check_upperifaces(ifupdownobj, ifaceobj,
ops, parent, followdependents):
return
if order == ifaceSchedulerFlags.INORDER:
# If inorder, run the iface first and then its dependents
cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
# Run lowerifaces or dependents
dlist = ifaceobj.lowerifaces
if dlist:
ifupdownobj.logger.debug('%s: found dependents %s'
%(ifacename, str(dlist)))
try:
if not followdependents:
# XXX: this is yet another extra step,
# but is needed for interfaces that are
# implicit dependents. even though we are asked to
# not follow dependents, we must follow the ones
# that dont have user given config. Because we own them
new_dlist = [d for d in dlist
if ifupdownobj.is_iface_noconfig(d)]
if new_dlist:
cls.run_iface_list(ifupdownobj, new_dlist, ops,
ifacename, order,
followdependents,
continueonfailure=False)
else:
cls.run_iface_list(ifupdownobj, dlist, ops,
ifacename, order,
followdependents,
continueonfailure=False)
except Exception, e:
if (ifupdownobj.ignore_error(str(e))):
pass
else:
# Dont bring the iface up if children did not come up
ifaceobj.set_state_n_status(ifaceState.NEW,
ifaceStatus.ERROR)
raise
if order == ifaceSchedulerFlags.POSTORDER:
cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
@classmethod
def run_iface_list(cls, ifupdownobj, ifacenames,
ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
followdependents=True, continueonfailure=True):
""" Runs interface list """
for ifacename in ifacenames:
try:
cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
order, followdependents)
except Exception, e:
if continueonfailure:
if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
traceback.print_tb(sys.exc_info()[2])
ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
pass
else:
if (ifupdownobj.ignore_error(str(e))):
pass
else:
raise Exception('%s : (%s)' %(ifacename, str(e)))
@classmethod
def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
followdependents=True, skip_root=False):
""" runs interface by traversing all nodes rooted at itself """
# Each ifacename can have a list of iface objects
ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
if not ifaceobjs:
raise Exception('%s: not found' %ifacename)
for ifaceobj in ifaceobjs:
if not skip_root:
# run the iface first and then its upperifaces
cls.run_iface_ops(ifupdownobj, ifaceobj, ops)
# Run upperifaces
ulist = ifaceobj.upperifaces
if ulist:
ifupdownobj.logger.debug('%s: found upperifaces %s'
%(ifacename, str(ulist)))
try:
cls.run_iface_list_upper(ifupdownobj, ulist, ops,
ifacename,
followdependents,
continueonfailure=True)
except Exception, e:
if (ifupdownobj.ignore_error(str(e))):
pass
else:
raise
@classmethod
def run_iface_list_upper(cls, ifupdownobj, ifacenames,
ops, parent=None, followdependents=True,
continueonfailure=True, skip_root=False):
""" Runs interface list """
for ifacename in ifacenames:
try:
cls.run_iface_graph_upper(ifupdownobj, ifacename, ops, parent,
followdependents, skip_root)
except Exception, e:
if continueonfailure:
if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
traceback.print_tb(sys.exc_info()[2])
ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
pass
else:
if (ifupdownobj.ignore_error(str(e))):
pass
else:
raise Exception('%s : (%s)' %(ifacename, str(e)))
@classmethod
def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
dependency_graph=None, indegrees=None,
order=ifaceSchedulerFlags.POSTORDER,
followdependents=True):
""" Runs iface dependeny graph by visiting all the nodes
Parameters:
-----------
ifupdownobj : ifupdown object (used for getting and updating iface
object state)
dependency_graph : dependency graph in adjacency list
format (contains more than one dependency graph)
ops : list of operations to perform eg ['pre-up', 'up', 'post-up']
indegrees : indegree array if present is used to determine roots
of the graphs in the dependency_graph
"""
if not ifupdownobj.ALL or not followdependents or len(ifacenames) == 1:
# If there is any interface that does exist, maybe it is a
# logical interface and we have to followupperifaces
followupperifaces = (True if
[i for i in ifacenames
if not ifupdownobj.link_exists(i)]
else False)
cls.run_iface_list(ifupdownobj, ifacenames, ops,
parent=None,order=order,
followdependents=followdependents)
if (not ifupdownobj.ALL and
(followdependents or followupperifaces) and 'up' in ops[0]):
# If user had given a set of interfaces to bring up
# try and execute 'up' on the upperifaces
ifupdownobj.logger.info('running upperifaces if available')
cls._STATE_CHECK = False
cls.run_iface_list_upper(ifupdownobj, ifacenames, ops,
skip_root=True)
cls._STATE_CHECK = True
return
run_queue = []
# Get a sorted list of all interfaces
if not indegrees:
indegrees = OrderedDict()
for ifacename in dependency_graph.keys():
indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
sorted_ifacenames = graph.topological_sort_graphs_all(dependency_graph,
dict(indegrees))
ifupdownobj.logger.debug('sorted ifacenames %s : '
%str(sorted_ifacenames))
# From the sorted list, pick interfaces that user asked
# and those that dont have any dependents first
[run_queue.append(ifacename)
for ifacename in sorted_ifacenames
if ifacename in ifacenames and
not indegrees.get(ifacename)]
ifupdownobj.logger.debug('graph roots (interfaces that dont have '
'dependents):' + ' %s' %str(run_queue))
cls.run_iface_list(ifupdownobj, run_queue, ops,
parent=None,order=order,
followdependents=followdependents)