mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
2586 lines
106 KiB
Python
2586 lines
106 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
|
|
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
|
#
|
|
# ifupdownMain --
|
|
# ifupdown main module
|
|
#
|
|
|
|
import re
|
|
import os
|
|
import logging
|
|
import itertools
|
|
import traceback
|
|
import pprint
|
|
|
|
from collections import OrderedDict
|
|
|
|
try:
|
|
import ifupdown2.lib.nlcache as nlcache
|
|
|
|
import ifupdown2.ifupdownaddons.mstpctlutil
|
|
|
|
import ifupdown2.nlmanager.ipnetwork as ipnetwork
|
|
|
|
import ifupdown2.ifupdown.policymanager
|
|
import ifupdown2.ifupdown.statemanager as statemanager
|
|
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
|
import ifupdown2.ifupdown.ifupdownconfig as ifupdownConfig
|
|
|
|
from ifupdown2.ifupdown.graph import *
|
|
from ifupdown2.ifupdown.iface import *
|
|
from ifupdown2.ifupdown.scheduler import *
|
|
from ifupdown2.ifupdown.exceptions import *
|
|
from ifupdown2.ifupdown.networkinterfaces import *
|
|
from ifupdown2.ifupdown.config import ADDON_MODULES_DIR, ADDONS_CONF_PATH, IFUPDOWN2_ADDON_DROPIN_FOLDER
|
|
except (ImportError, ModuleNotFoundError):
|
|
import lib.nlcache as nlcache
|
|
|
|
import ifupdownaddons.mstpctlutil
|
|
|
|
import nlmanager.ipnetwork as ipnetwork
|
|
|
|
import ifupdown.ifupdownflags
|
|
import ifupdown.policymanager
|
|
import ifupdown.statemanager as statemanager
|
|
import ifupdown.ifupdownflags as ifupdownflags
|
|
import ifupdown.ifupdownconfig as ifupdownConfig
|
|
|
|
from ifupdown.graph import *
|
|
from ifupdown.iface import *
|
|
from ifupdown.scheduler import *
|
|
from ifupdown.exceptions import *
|
|
from ifupdown.networkinterfaces import *
|
|
from ifupdown.config import ADDON_MODULES_DIR, ADDONS_CONF_PATH, IFUPDOWN2_ADDON_DROPIN_FOLDER
|
|
|
|
|
|
"""
|
|
.. module:: ifupdownmain
|
|
:synopsis: main module for ifupdown package
|
|
|
|
.. moduleauthor:: Roopa Prabhu <roopa@cumulusnetworks.com>
|
|
|
|
"""
|
|
|
|
_tickmark = '\u2713'
|
|
_crossmark = '\u2717'
|
|
_success_sym = '(%s)' %_tickmark
|
|
_error_sym = '(%s)' %_crossmark
|
|
|
|
class ifupdownMainFlags():
|
|
COMPAT_EXEC_SCRIPTS = False
|
|
STATEMANAGER_ENABLE = True
|
|
STATEMANAGER_UPDATE = True
|
|
ADDONS_ENABLE = False
|
|
DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
|
|
SCHED_SKIP_CHECK_UPPERIFACES = False
|
|
CHECK_SHARED_DEPENDENTS = True
|
|
|
|
class ifacePrivFlags():
|
|
# priv flags to mark iface objects
|
|
BUILTIN = False
|
|
NOCONFIG = False
|
|
|
|
def __init__(self, builtin=False, noconfig=False):
|
|
self.BUILTIN = builtin
|
|
self.NOCONFIG = noconfig
|
|
|
|
class ifupdownMain:
|
|
""" ifupdown2 main class """
|
|
|
|
scripts_dir = '/etc/network'
|
|
addon_modules_dir = ADDON_MODULES_DIR
|
|
addon_modules_configfile = ADDONS_CONF_PATH
|
|
|
|
# Handlers for ops that ifupdown2 owns
|
|
def run_up(self, ifaceobj):
|
|
# Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs).
|
|
# there is no real interface behind it
|
|
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
|
|
return
|
|
if ((ifaceobj.link_kind & ifaceLinkKind.VRF) or
|
|
(ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
|
|
self._keep_link_down(ifaceobj)
|
|
return
|
|
if self._delay_admin_state:
|
|
self._delay_admin_state_iface_queue.append(ifaceobj.name)
|
|
return
|
|
# If this object is a link slave, ie its link is controlled
|
|
# by its link master interface, then dont set the link state.
|
|
# But do allow user to change state of the link if the interface
|
|
# is already with its link master (hence the master check).
|
|
if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
|
|
return
|
|
if not self.link_exists(ifaceobj.name):
|
|
return
|
|
if self._keep_link_down(ifaceobj):
|
|
return
|
|
try:
|
|
self.netlink.link_up(ifaceobj.name)
|
|
except Exception:
|
|
if ifaceobj.addr_method == 'manual':
|
|
pass
|
|
else:
|
|
raise
|
|
|
|
def _keep_link_down(self, ifaceobj):
|
|
if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
|
|
# user has asked to explicitly keep the link down,
|
|
# so, force link down
|
|
self.logger.info('%s: keeping link down due to user config' %ifaceobj.name)
|
|
self.netlink.link_down(ifaceobj.name)
|
|
return True
|
|
return False
|
|
|
|
def run_down(self, ifaceobj):
|
|
if ((ifaceobj.link_kind & ifaceLinkKind.VRF) or
|
|
(ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
|
|
return
|
|
# Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs)
|
|
# there is no real interface behind it
|
|
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
|
|
return
|
|
if self._delay_admin_state:
|
|
self._delay_admin_state_iface_queue.append(ifaceobj.name)
|
|
return
|
|
# If this object is a link slave, ie its link is controlled
|
|
# by its link master interface, then dont set the link state.
|
|
# But do allow user to change state of the link if the interface
|
|
# is already with its link master (hence the master check).
|
|
if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
|
|
return
|
|
if not self.link_exists(ifaceobj.name):
|
|
return
|
|
try:
|
|
if not ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK:
|
|
# set intf down (except loopback)
|
|
self.netlink.link_down(ifaceobj.name)
|
|
else:
|
|
self.logger.info("%s: ifupdown2 cannot bring loopback interface down" % ifaceobj.name)
|
|
except Exception:
|
|
if ifaceobj.addr_method == 'manual':
|
|
pass
|
|
else:
|
|
raise
|
|
|
|
# ifupdown object interface operation handlers
|
|
ops_handlers = OrderedDict([('up', run_up),
|
|
('down', run_down)])
|
|
|
|
def run_sched_ifaceobj_posthook(self, ifaceobj, op):
|
|
if (ifaceobj.priv_flags and (ifaceobj.priv_flags.BUILTIN or
|
|
ifaceobj.priv_flags.NOCONFIG)):
|
|
return
|
|
if self.flags.STATEMANAGER_UPDATE:
|
|
self.statemanager.ifaceobj_sync(ifaceobj, op)
|
|
|
|
# ifupdown object interface scheduler pre and posthooks
|
|
sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
|
|
|
|
def reset_ifupdown2(self):
|
|
self.modules = OrderedDict({})
|
|
self.module_attrs = {}
|
|
|
|
ifaceScheduler.reset()
|
|
try:
|
|
ifupdown2.ifupdown.statemanager.reset()
|
|
ifupdown2.ifupdown.policymanager.reset()
|
|
ifupdown2.ifupdown.ifupdownflags.reset()
|
|
ifupdownConfig.reset()
|
|
ifupdown2.ifupdownaddons.mstpctlutil.mstpctlutil.reset()
|
|
except Exception:
|
|
try:
|
|
ifupdown.statemanager.reset()
|
|
ifupdown.policymanager.reset()
|
|
ifupdown.ifupdownflags.reset()
|
|
ifupdownConfig.reset()
|
|
ifupdownaddons.mstpctlutil.mstpctlutil.reset()
|
|
except Exception:
|
|
pass
|
|
|
|
def ignore_error(self, errmsg):
|
|
if (ifupdownflags.flags.FORCE == True or re.search(r'exists', errmsg,
|
|
re.IGNORECASE | re.MULTILINE) is not None):
|
|
return True
|
|
return False
|
|
|
|
def log_warn(self, str):
|
|
if self.ignore_error(str) == False:
|
|
if self.logger.getEffectiveLevel() == logging.DEBUG:
|
|
traceback.print_stack()
|
|
traceback.print_exc()
|
|
self.logger.warning(str)
|
|
pass
|
|
|
|
def log_error(self, str):
|
|
if self.ignore_error(str) == False:
|
|
raise Exception(str)
|
|
else:
|
|
pass
|
|
|
|
def link_exists(self, ifacename):
|
|
return os.path.exists('/sys/class/net/%s' %ifacename)
|
|
|
|
def __init__(self, config={}, args=None,
|
|
daemon=False, force=False, dryrun=False, nowait=False,
|
|
perfmode=False, withdepends=False, njobs=1,
|
|
cache=False, addons_enable=True, statemanager_enable=True,
|
|
interfacesfile='/etc/network/interfaces',
|
|
interfacesfileiobuf=None,
|
|
interfacesfileformat='native',
|
|
withdefaults=False):
|
|
"""This member function initializes the ifupdownmain object.
|
|
|
|
Kwargs:
|
|
config (dict): config dict from /etc/network/ifupdown2/ifupdown2.conf
|
|
force (bool): force interface configuration
|
|
dryrun (bool): dryrun interface configuration
|
|
withdepends (bool): apply interface configuration on all depends
|
|
interfacesfile (str): interfaces file. default is /etc/network/interfaces
|
|
interfacesfileformat (str): default is 'native'. Other choices are 'json'
|
|
|
|
Raises:
|
|
AttributeError, KeyError """
|
|
|
|
modulename = self.__class__.__name__
|
|
self.logger = logging.getLogger('ifupdown.' + modulename)
|
|
|
|
if daemon:
|
|
self.reset_ifupdown2()
|
|
else:
|
|
# init nlcache with appropriate log level
|
|
nlcache.NetlinkListenerWithCache.init(logging.DEBUG if args.nldebug else logging.WARNING)
|
|
|
|
# start netlink listener and cache link/addr/netconf dumps
|
|
nlcache.NetlinkListenerWithCache.get_instance().start()
|
|
|
|
# save reference to nlcache
|
|
self.netlink = nlcache.NetlinkListenerWithCache.get_instance()
|
|
self.netlink.reset_errorq()
|
|
|
|
# iface dictionary in the below format:
|
|
# { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
|
|
# eg:
|
|
# { 'swp1' : [<iface swp1>, <iface swp2> ..] }
|
|
#
|
|
# Each ifaceobject corresponds to a configuration block for
|
|
# that interface
|
|
# The value in the dictionary is a list because the network
|
|
# interface configuration file supports more than one iface section
|
|
# in the interfaces file
|
|
self.ifaceobjdict = OrderedDict()
|
|
|
|
# iface dictionary representing the curr running state of an iface
|
|
# in the below format:
|
|
# {'<ifacename>' : <ifaceobject>}
|
|
self.ifaceobjcurrdict = OrderedDict()
|
|
|
|
# Dictionary representing operation and modules
|
|
# for every operation
|
|
self.module_ops = OrderedDict([('pre-up', []),
|
|
('up', []),
|
|
('post-up', []),
|
|
('query-checkcurr', []),
|
|
('query-running', []),
|
|
('query-dependency', []),
|
|
('query', []),
|
|
('query-raw', []),
|
|
('pre-down', []),
|
|
('down', []),
|
|
('post-down', [])])
|
|
|
|
# For old style /etc/network/ bash scripts
|
|
self.script_ops = OrderedDict([('pre-up', []),
|
|
('up', []),
|
|
('post-up', []),
|
|
('pre-down', []),
|
|
('down', []),
|
|
('post-down', [])])
|
|
|
|
|
|
self.logger = logging.getLogger('ifupdown')
|
|
ifupdownflags.flags.FORCE = force
|
|
ifupdownflags.flags.DRYRUN = dryrun
|
|
ifupdownflags.flags.WITHDEFAULTS = withdefaults
|
|
ifupdownflags.flags.NOWAIT = nowait
|
|
ifupdownflags.flags.PERFMODE = perfmode
|
|
ifupdownflags.flags.CACHE = cache
|
|
ifupdownflags.flags.WITH_DEPENDS = withdepends
|
|
|
|
# Can be used to provide hints for caching
|
|
ifupdownflags.flags.CACHE_FLAGS = 0x0
|
|
|
|
self.flags = ifupdownMainFlags()
|
|
|
|
self.flags.STATEMANAGER_ENABLE = statemanager_enable
|
|
self.interfacesfile = interfacesfile
|
|
self.interfacesfileiobuf = interfacesfileiobuf
|
|
self.interfacesfileformat = interfacesfileformat
|
|
self.config = config
|
|
self.logger.debug(self.config)
|
|
self.blacklisted_ifaces_present = False
|
|
|
|
self.type = ifaceType.UNKNOWN
|
|
|
|
self.flags.DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
|
|
self.flags.ADDONS_ENABLE = addons_enable
|
|
|
|
self.ifaces = OrderedDict()
|
|
self.njobs = njobs
|
|
self.pp = pprint.PrettyPrinter(indent=4)
|
|
self.modules = OrderedDict({})
|
|
self.module_attrs = {}
|
|
self.overridden_ifupdown_scripts = []
|
|
|
|
if self.config.get('addon_python_modules_support', '1') == '1':
|
|
self.load_addon_modules(self.addon_modules_dir)
|
|
if self.config.get('addon_scripts_support', '0') == '1':
|
|
self.load_scripts(self.scripts_dir)
|
|
self.dependency_graph = OrderedDict({})
|
|
|
|
self._cache_no_repeats = {}
|
|
|
|
# initialize global config object with config passed by the user
|
|
# This makes config available to addon modules
|
|
ifupdownConfig.config = self.config
|
|
statemanager.statemanager_api.init()
|
|
|
|
if self.flags.STATEMANAGER_ENABLE:
|
|
self.statemanager = statemanager.statemanager_api
|
|
try:
|
|
self.statemanager.read_saved_state()
|
|
except Exception as e:
|
|
# if read_saved_state fails, state file might be corrupt.
|
|
# Ignore old state and continue
|
|
self.logger.warning('error reading state (%s)' %str(e))
|
|
import traceback
|
|
traceback.print_exc()
|
|
else:
|
|
self.flags.STATEMANAGER_UPDATE = False
|
|
self._delay_admin_state = True if self.config.get(
|
|
'delay_admin_state_change', '0') == '1' else False
|
|
self._delay_admin_state_iface_queue = []
|
|
if self._delay_admin_state:
|
|
self.logger.info('\'delay_admin_state_change\' is set. admin ' +
|
|
'state changes will be delayed till the end.')
|
|
|
|
self._link_master_slave = True if self.config.get(
|
|
'link_master_slave', '0') == '1' else False
|
|
if self._link_master_slave:
|
|
self.logger.info('\'link_master_slave\' is set. slave admin ' +
|
|
'state changes will be delayed till the ' +
|
|
'masters admin state change.')
|
|
|
|
# squash iface objects for same interface both internal and
|
|
# external representation. It is off by default.
|
|
self._ifaceobj_squash = True if self.config.get(
|
|
'ifaceobj_squash', '0') == '1' else False
|
|
|
|
# squash iface objects for same interface internal
|
|
# representation only. External representation as seen by ifquery
|
|
# will continue to see multiple iface stanzas if it was specified
|
|
# that way by the user. It is on by default.
|
|
self._ifaceobj_squash_internal = True if self.config.get(
|
|
'ifaceobj_squash_internal', '1') == '1' else False
|
|
|
|
self.mgmt_iface_default_prefix = self._get_mgmt_iface_default_prefix()
|
|
self.logger.info('using mgmt iface default prefix %s' %self.mgmt_iface_default_prefix)
|
|
|
|
self.validate_keywords = {
|
|
'<mac>': self._keyword_mac,
|
|
'<text>': self._keyword_text,
|
|
'<ipv4>': self._keyword_ipv4,
|
|
'<ipv6>': self._keyword_ipv6,
|
|
'<ip>': self._keyword_ip,
|
|
'<number>': self._keyword_number,
|
|
'<interface>': self._keyword_interface,
|
|
'<ipv4-vrf-text>': self._keyword_ipv4_vrf_text,
|
|
'<number-ipv4-list>': self._keyword_number_ipv4_list,
|
|
'<interface-list>': self._keyword_interface_list,
|
|
'<ipv4/prefixlen>': self._keyword_ipv4_prefixlen,
|
|
'<ipv6/prefixlen>': self._keyword_ipv6_prefixlen,
|
|
'<ip/prefixlen>': self._keyword_ip_prefixlen,
|
|
'<number-range-list>': self._keyword_number_range_list,
|
|
'<number-comma-range-list>': self._keyword_number_comma_range_list,
|
|
'<interface-range-list>': self._keyword_interface_range_list,
|
|
'<interface-range-list-multiple-of-16>': self._keyword_interface_range_list_multiple_of_16,
|
|
'<mac-ip/prefixlen-list>': self._keyword_mac_ip_prefixlen_list,
|
|
'<number-interface-list>': self._keyword_number_interface_list,
|
|
'<interface-yes-no-list>': self._keyword_interface_yes_no_list,
|
|
'<interface-on-off-list>': self._keyword_interface_on_off_list,
|
|
'<interface-yes-no-0-1-list>': self._keyword_interface_yes_no_0_1_list,
|
|
'<interface-disabled-automatic-enabled>': self._keyword_interface_disabled_automatic_enabled_list,
|
|
'<interface-yes-no-auto-list>': self._keyword_interface_yes_no_auto_list,
|
|
'<interface-l2protocol-tunnel-list>': self._keyword_interface_l2protocol_tunnel_list
|
|
}
|
|
|
|
def _get_mgmt_iface_default_prefix(self):
|
|
mgmt_iface_default_prefix = None
|
|
try:
|
|
mgmt_iface_default_prefix = ifupdown2.ifupdown.policymanager.policymanager_api.get_module_globals(
|
|
module_name='main', attr='mgmt_intf_prefix'
|
|
)
|
|
except Exception:
|
|
try:
|
|
mgmt_iface_default_prefix = ifupdown.policymanager.policymanager_api.get_module_globals(
|
|
module_name='main', attr='mgmt_intf_prefix'
|
|
)
|
|
except Exception:
|
|
pass
|
|
if not mgmt_iface_default_prefix:
|
|
mgmt_iface_default_prefix = "eth"
|
|
|
|
return mgmt_iface_default_prefix
|
|
|
|
def link_master_slave_ignore_error(self, errorstr):
|
|
# If link master slave flag is set,
|
|
# there may be cases where the lowerdev may not be
|
|
# up resulting in 'Network is down' error
|
|
# This can happen if the lowerdev is a LINK_SLAVE
|
|
# of another interface which is not up yet
|
|
# example of such a case:
|
|
# bringing up a vlan on a bond interface and the bond
|
|
# is a LINK_SLAVE of a bridge (in other words the bond is
|
|
# part of a bridge) which is not up yet
|
|
if self._link_master_slave:
|
|
if 'Network is down' in errorstr:
|
|
return True
|
|
return False
|
|
|
|
def get_ifaceobjs(self, ifacename, all=False):
|
|
if all:
|
|
return dict(self.ifaceobjdict)
|
|
return self.ifaceobjdict.get(ifacename)
|
|
|
|
def get_ifaceobjs_saved(self, ifacename):
|
|
""" Return ifaceobjects from statemanager """
|
|
if self.flags.STATEMANAGER_ENABLE:
|
|
return self.statemanager.get_ifaceobjs(ifacename)
|
|
else:
|
|
return None
|
|
|
|
def get_ifaceobj_first(self, ifacename):
|
|
ifaceobjs = self.get_ifaceobjs(ifacename)
|
|
if ifaceobjs:
|
|
return ifaceobjs[0]
|
|
return None
|
|
|
|
def get_ifacenames(self):
|
|
return list(self.ifaceobjdict.keys())
|
|
|
|
def get_iface_obj_last(self, ifacename):
|
|
return self.ifaceobjdict.get(ifacename)[-1]
|
|
|
|
|
|
def must_follow_upperifaces(self, ifacename):
|
|
#
|
|
# XXX: This bleeds the knowledge of iface
|
|
# types in the infrastructure module.
|
|
# Cant think of a better fix at the moment.
|
|
# In future maybe the module can set a flag
|
|
# to indicate if we should follow upperifaces
|
|
#
|
|
ifaceobj = self.get_ifaceobj_first(ifacename)
|
|
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
|
|
return False
|
|
return True
|
|
|
|
def create_n_save_ifaceobj(self, ifacename, priv_flags=None,
|
|
increfcnt=False):
|
|
""" creates a iface object and adds it to the iface dictionary """
|
|
ifaceobj = iface()
|
|
ifaceobj.name = ifacename
|
|
ifaceobj.priv_flags = priv_flags
|
|
ifaceobj.auto = True
|
|
if not self._link_master_slave:
|
|
ifaceobj.link_type = ifaceLinkType.LINK_NA
|
|
if increfcnt:
|
|
ifaceobj.inc_refcnt()
|
|
self.ifaceobjdict[ifacename] = [ifaceobj]
|
|
return ifaceobj
|
|
|
|
def create_n_save_ifaceobjcurr(self, ifaceobj):
|
|
""" creates a copy of iface object and adds it to the iface
|
|
dict containing current iface objects
|
|
"""
|
|
ifaceobjcurr = iface()
|
|
ifaceobjcurr.name = ifaceobj.name
|
|
ifaceobjcurr.type = ifaceobj.type
|
|
ifaceobjcurr.lowerifaces = ifaceobj.lowerifaces
|
|
ifaceobjcurr.priv_flags = copy.deepcopy(ifaceobj.priv_flags)
|
|
ifaceobjcurr.auto = ifaceobj.auto
|
|
self.ifaceobjcurrdict.setdefault(ifaceobj.name,
|
|
[]).append(ifaceobjcurr)
|
|
return ifaceobjcurr
|
|
|
|
def get_ifaceobjcurr(self, ifacename, idx=0):
|
|
ifaceobjlist = self.ifaceobjcurrdict.get(ifacename)
|
|
if not ifaceobjlist:
|
|
return None
|
|
if not idx:
|
|
return ifaceobjlist
|
|
else:
|
|
return ifaceobjlist[idx]
|
|
|
|
def get_iface_refcnt(self, ifacename):
|
|
""" Return iface ref count """
|
|
max = 0
|
|
ifaceobjs = self.get_ifaceobjs(ifacename)
|
|
if not ifaceobjs:
|
|
return 0
|
|
for i in ifaceobjs:
|
|
if i.refcnt > max:
|
|
max = i.refcnt
|
|
return max
|
|
|
|
def is_iface_builtin_byname(self, ifacename):
|
|
""" Returns true if iface name is a builtin interface.
|
|
|
|
A builtin interface is an interface which ifupdown understands.
|
|
The following are currently considered builtin ifaces:
|
|
- vlan interfaces in the format <ifacename>.<vlanid>
|
|
"""
|
|
return '.' in ifacename
|
|
|
|
def is_ifaceobj_builtin(self, ifaceobj):
|
|
""" Returns true if iface name is a builtin interface.
|
|
|
|
A builtin interface is an interface which ifupdown understands.
|
|
The following are currently considered builtin ifaces:
|
|
- vlan interfaces in the format <ifacename>.<vlanid>
|
|
"""
|
|
if (ifaceobj.priv_flags and ifaceobj.priv_flags.BUILTIN):
|
|
return True
|
|
return False
|
|
|
|
def is_ifaceobj_noconfig(self, ifaceobj):
|
|
""" Returns true if iface object did not have a user defined config.
|
|
|
|
These interfaces appear only when they are dependents of interfaces
|
|
which have user defined config
|
|
"""
|
|
return (ifaceobj.priv_flags and ifaceobj.priv_flags.NOCONFIG)
|
|
|
|
def is_iface_noconfig(self, ifacename):
|
|
""" Returns true if iface has no config """
|
|
|
|
ifaceobj = self.get_ifaceobj_first(ifacename)
|
|
if not ifaceobj: return True
|
|
return self.is_ifaceobj_noconfig(ifaceobj)
|
|
|
|
def check_shared_dependents(self, ifaceobj, dlist):
|
|
""" ABSOLETE: Check if dlist intersects with any other
|
|
interface with slave dependents.
|
|
example: bond and bridges.
|
|
This function logs such errors """
|
|
setdlist = set(dlist)
|
|
for ifacename, ifacedlist in list(self.dependency_graph.items()):
|
|
if not ifacedlist:
|
|
continue
|
|
check_depends = False
|
|
iobjs = self.get_ifaceobjs(ifacename)
|
|
if not iobjs:
|
|
continue
|
|
for i in iobjs:
|
|
if (i.dependency_type == ifaceDependencyType.MASTER_SLAVE):
|
|
check_depends = True
|
|
if check_depends:
|
|
common = set(ifacedlist).intersection(setdlist)
|
|
if common:
|
|
self.logger.error('misconfig..?. iface %s and %s '
|
|
%(ifaceobj.name, ifacename) +
|
|
'seem to share dependents/ports %s' %str(list(common)))
|
|
|
|
def _set_iface_role(self, ifaceobj, role, upperifaceobj):
|
|
if (self.flags.CHECK_SHARED_DEPENDENTS and
|
|
(ifaceobj.role & ifaceRole.SLAVE) and
|
|
(role == ifaceRole.SLAVE) and (upperifaceobj.role & ifaceRole.MASTER)):
|
|
self.logger.error("misconfig..? %s %s is enslaved to multiple interfaces %s"
|
|
% (ifaceobj.name,
|
|
ifaceLinkPrivFlags.get_str(ifaceobj.link_privflags), str(ifaceobj.upperifaces)))
|
|
ifaceobj.set_status(ifaceStatus.ERROR)
|
|
return
|
|
ifaceobj.role = role
|
|
|
|
def _set_iface_role_n_kind(self, ifaceobj, upperifaceobj):
|
|
if (upperifaceobj.link_kind & ifaceLinkKind.BOND):
|
|
self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
|
|
ifaceobj.link_privflags |= ifaceLinkPrivFlags.BOND_SLAVE
|
|
|
|
if (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
|
|
self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
|
|
ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_PORT
|
|
|
|
if (ifaceobj.link_kind & ifaceLinkKind.VXLAN) \
|
|
and (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
|
|
upperifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
|
|
|
|
# vrf masters get processed after slaves, which means
|
|
# check both link_kind vrf and vrf slave
|
|
if ((upperifaceobj.link_kind & ifaceLinkKind.VRF) or
|
|
(ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
|
|
self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
|
|
ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
|
|
if self._link_master_slave:
|
|
if upperifaceobj.link_type == ifaceLinkType.LINK_MASTER:
|
|
ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
|
|
else:
|
|
upperifaceobj.link_type = ifaceLinkType.LINK_NA
|
|
ifaceobj.link_type = ifaceLinkType.LINK_NA
|
|
|
|
def dump_iface_dependency_info(self):
|
|
""" debug funtion to print raw dependency
|
|
info - lower and upper devices"""
|
|
|
|
for ifacename, ifaceobjs in self.ifaceobjdict.items():
|
|
iobj = ifaceobjs[0]
|
|
self.logger.info("%s: refcnt: %d, lower: %s, upper: %s" %(ifacename,
|
|
self.get_iface_refcnt(ifacename),
|
|
str(iobj.lowerifaces) if iobj.lowerifaces else [],
|
|
str(iobj.upperifaces) if iobj.upperifaces else []))
|
|
|
|
|
|
def preprocess_dependency_list(self, upperifaceobj, dlist, ops):
|
|
""" We go through the dependency list and
|
|
delete or add interfaces from the interfaces dict by
|
|
applying the following rules:
|
|
if flag DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True:
|
|
we only consider devices whose configuration was
|
|
specified in the network interfaces file. We delete
|
|
any interface whose config was not specified except
|
|
for vlan devices. vlan devices get special treatment.
|
|
Even if they are not present they are created and added
|
|
to the ifacesdict
|
|
elif flag DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False:
|
|
we create objects for all dependent devices that are not
|
|
present in the ifacesdict
|
|
"""
|
|
del_list = []
|
|
|
|
for d in dlist:
|
|
dilist = self.get_ifaceobjs(d)
|
|
if not dilist:
|
|
ni = None
|
|
if self.is_iface_builtin_byname(d):
|
|
ni = self.create_n_save_ifaceobj(d,
|
|
ifacePrivFlags(True, True), True)
|
|
elif not self.flags.DELETE_DEPENDENT_IFACES_WITH_NOCONFIG:
|
|
ni = self.create_n_save_ifaceobj(d,
|
|
ifacePrivFlags(False, True), True)
|
|
else:
|
|
del_list.append(d)
|
|
if ni:
|
|
ni.add_to_upperifaces(upperifaceobj.name)
|
|
self._set_iface_role_n_kind(ni, upperifaceobj)
|
|
else:
|
|
for di in dilist:
|
|
di.inc_refcnt()
|
|
di.add_to_upperifaces(upperifaceobj.name)
|
|
self._set_iface_role_n_kind(di, upperifaceobj)
|
|
for d in del_list:
|
|
dlist.remove(d)
|
|
|
|
def preprocess_upperiface(self, lowerifaceobj, ulist, ops):
|
|
for u in ulist:
|
|
if (lowerifaceobj.upperifaces and
|
|
u in lowerifaceobj.upperifaces):
|
|
continue
|
|
lowerifaceobj.add_to_upperifaces(u)
|
|
uifacelist = self.get_ifaceobjs(u)
|
|
if uifacelist:
|
|
for ui in uifacelist:
|
|
lowerifaceobj.inc_refcnt()
|
|
self._set_iface_role_n_kind(lowerifaceobj, ui)
|
|
ui.add_to_lowerifaces(lowerifaceobj.name)
|
|
|
|
def query_lowerifaces(self, ifaceobj, ops, ifacenames, old_ifaceobjs):
|
|
""" Gets iface dependents by calling into respective modules """
|
|
ret_dlist = []
|
|
# Get dependents for interface by querying respective modules
|
|
for module in list(self.modules.values()):
|
|
try:
|
|
if ops[0] == 'query-running':
|
|
if (not hasattr(module,
|
|
'get_dependent_ifacenames_running')):
|
|
continue
|
|
dlist = module.get_dependent_ifacenames_running(ifaceobj)
|
|
else:
|
|
if (not hasattr(module, 'get_dependent_ifacenames')):
|
|
continue
|
|
dlist = module.get_dependent_ifacenames(ifaceobj,
|
|
ifacenames, old_ifaceobjs)
|
|
except Exception as e:
|
|
self.logger.warning("%s: %s: error getting dependent interfaces (%s)" % (ifaceobj.name, module, str(e)))
|
|
dlist = None
|
|
if dlist: ret_dlist.extend(dlist)
|
|
return list(set(ret_dlist))
|
|
|
|
def query_upperifaces(self, ifaceobj, ops, ifacenames, type=None):
|
|
""" Gets iface upperifaces by calling into respective modules """
|
|
ret_ulist = []
|
|
|
|
# Get upperifaces for interface by querying respective modules
|
|
for module in list(self.modules.values()):
|
|
try:
|
|
if ops[0] == 'query-running':
|
|
if (not hasattr(module,
|
|
'get_upper_ifacenames_running')):
|
|
continue
|
|
ulist = module.get_upper_ifacenames_running(ifaceobj)
|
|
else:
|
|
if (not hasattr(module, 'get_upper_ifacenames')):
|
|
continue
|
|
ulist = module.get_upper_ifacenames(ifaceobj, ifacenames)
|
|
except Exception as e:
|
|
self.logger.warning('%s: error getting upper interfaces (%s)'
|
|
%(ifaceobj.name, str(e)))
|
|
ulist = None
|
|
pass
|
|
if ulist: ret_ulist.extend(ulist)
|
|
return list(set(ret_ulist))
|
|
|
|
def _remove_circular_veth_dependencies(self, ifaceobj, dlist):
|
|
# if ifaceobj isn't a veth link, ignore it.
|
|
if ifaceobj.get_attr_value_first('link-type') != "veth":
|
|
return
|
|
|
|
for diface in dlist:
|
|
difaceobj = self.get_ifaceobj_first(diface)
|
|
# If the dependent iface isn't a veth link - which shouldn't
|
|
# happen - ignore it to be save.
|
|
if not difaceobj or (difaceobj and difaceobj.get_attr_value_first('link-type') != "veth"):
|
|
continue
|
|
|
|
# If the peer has a desired peer name set and this is us,
|
|
# see if the peer has a dependency to us too and remove our
|
|
# redundant dependency to the peer.
|
|
diface_peer_name = difaceobj.get_attr_value_first('veth-peer-name')
|
|
if diface_peer_name and diface_peer_name == ifaceobj.name:
|
|
peer_dlist = difaceobj.lowerifaces
|
|
if not peer_dlist:
|
|
# Not list of dependent interface on the peer.
|
|
continue
|
|
|
|
# We aleady are in the peers dlist, don't add dependcy from us to peer
|
|
if ifaceobj.name in peer_dlist:
|
|
dlist.remove(difaceobj.name)
|
|
|
|
def populate_dependency_info(self, ops, ifacenames=None, old_ifaceobjs=False):
|
|
""" recursive function to generate iface dependency info """
|
|
|
|
if not ifacenames:
|
|
ifacenames = list(self.ifaceobjdict.keys())
|
|
|
|
iqueue = deque(ifacenames)
|
|
while iqueue:
|
|
i = iqueue.popleft()
|
|
# Go through all modules and find dependent ifaces
|
|
dlist = None
|
|
ulist = None
|
|
ifaceobjs = self.get_ifaceobjs(i)
|
|
if not ifaceobjs:
|
|
continue
|
|
dependents_processed = False
|
|
|
|
# Store all dependency info in the first ifaceobj
|
|
# but get dependency info from all ifaceobjs
|
|
ifaceobj = ifaceobjs[0]
|
|
for iobj in ifaceobjs:
|
|
ulist = self.query_upperifaces(iobj, ops, ifacenames)
|
|
if iobj.lowerifaces:
|
|
dependents_processed = True
|
|
break
|
|
dlist = self.query_lowerifaces(iobj, ops, ifacenames, old_ifaceobjs)
|
|
if dlist:
|
|
break
|
|
if ulist:
|
|
self.preprocess_upperiface(ifaceobj, ulist, ops)
|
|
if dependents_processed:
|
|
continue
|
|
if dlist:
|
|
self._remove_circular_veth_dependencies(ifaceobj, dlist)
|
|
self.preprocess_dependency_list(ifaceobj,
|
|
dlist, ops)
|
|
ifaceobj.lowerifaces = dlist
|
|
[iqueue.append(d) for d in dlist]
|
|
#if not self.dependency_graph.get(i):
|
|
# self.dependency_graph[i] = dlist
|
|
|
|
for i in list(self.ifaceobjdict.keys()):
|
|
iobj = self.get_ifaceobj_first(i)
|
|
if (not iobj.link_kind and
|
|
not (iobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK) and
|
|
iobj.name == 'lo'):
|
|
iobj.link_privflags |= ifaceLinkPrivFlags.LOOPBACK
|
|
if iobj.name.startswith(self.mgmt_iface_default_prefix):
|
|
self.logger.debug('%s: marking interface with mgmt flag' %iobj.name)
|
|
iobj.link_privflags |= ifaceLinkPrivFlags.MGMT_INTF
|
|
if iobj.lowerifaces:
|
|
self.dependency_graph[i] = iobj.lowerifaces
|
|
else:
|
|
self.dependency_graph[i] = []
|
|
|
|
if not self.blacklisted_ifaces_present:
|
|
return
|
|
|
|
# Walk through the dependency graph and remove blacklisted
|
|
# interfaces that were picked up as dependents
|
|
for i in list(self.dependency_graph.keys()):
|
|
ifaceobj = self.get_ifaceobj_first(i)
|
|
if not ifaceobj:
|
|
continue
|
|
|
|
if ifaceobj.blacklisted and not ifaceobj.upperifaces:
|
|
# if blacklisted and was not picked up as a
|
|
# dependent of a upper interface, delete the
|
|
# interface from the dependency graph
|
|
dlist = ifaceobj.lowerifaces
|
|
if dlist:
|
|
for d in dlist:
|
|
difaceobjs = self.get_ifaceobjs(d)
|
|
if not difaceobjs:
|
|
continue
|
|
try:
|
|
for d in difaceobjs:
|
|
d.dec_refcnt()
|
|
d.upperifaces.remove(i)
|
|
except Exception:
|
|
self.logger.debug('error removing %s from %s upperifaces' %(i, d))
|
|
pass
|
|
self.logger.debug("populate_dependency_info: deleting blacklisted interface %s" %i)
|
|
del self.dependency_graph[i]
|
|
continue
|
|
|
|
def _check_config_no_repeats(self, ifaceobj):
|
|
""" check if object has an attribute that is
|
|
restricted to a single object in the system.
|
|
if yes, warn and return """
|
|
for k,v in list(self._cache_no_repeats.items()):
|
|
iv = ifaceobj.config.get(k)
|
|
if iv and iv[0] == v:
|
|
self.logger.error('ignoring interface %s. ' %ifaceobj.name +
|
|
'Only one object with attribute ' +
|
|
'\'%s %s\' allowed.' %(k, v))
|
|
return True
|
|
for k, v in list(self.config.get('no_repeats', {}).items()):
|
|
iv = ifaceobj.config.get(k)
|
|
if iv and iv[0] == v:
|
|
self._cache_no_repeats[k] = v
|
|
return False
|
|
|
|
def _save_iface_squash(self, ifaceobj):
|
|
""" squash ifaceobjects belonging to same iface
|
|
into a single object """
|
|
if self._check_config_no_repeats(ifaceobj):
|
|
return
|
|
ifaceobj.priv_flags = ifacePrivFlags()
|
|
if not self._link_master_slave:
|
|
ifaceobj.link_type = ifaceLinkType.LINK_NA
|
|
currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
|
|
if not currentifaceobjlist:
|
|
self.ifaceobjdict[ifaceobj.name] = [ifaceobj]
|
|
return
|
|
if ifaceobj.compare(currentifaceobjlist[0]):
|
|
self.logger.warning('duplicate interface %s found' %ifaceobj.name)
|
|
return
|
|
for obj in self.ifaceobjdict[ifaceobj.name]:
|
|
if obj.type == ifaceobj.type:
|
|
obj.squash(ifaceobj)
|
|
return
|
|
self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
|
|
|
|
def _save_iface(self, ifaceobj):
|
|
if self._check_config_no_repeats(ifaceobj):
|
|
return
|
|
ifaceobj.priv_flags = ifacePrivFlags()
|
|
if not self._link_master_slave:
|
|
ifaceobj.link_type = ifaceLinkType.LINK_NA
|
|
currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
|
|
if not currentifaceobjlist:
|
|
self.ifaceobjdict[ifaceobj.name]= [ifaceobj]
|
|
if not self._ifaceobj_squash:
|
|
ifaceobj.flags |= ifaceobj.YOUNGEST_SIBLING
|
|
return
|
|
if ifaceobj.compare(currentifaceobjlist[0]):
|
|
self.logger.warning('duplicate interface %s found' %ifaceobj.name)
|
|
return
|
|
if currentifaceobjlist[0].type == ifaceobj.type:
|
|
currentifaceobjlist[0].flags |= ifaceobj.HAS_SIBLINGS
|
|
ifaceobj.flags |= ifaceobj.HAS_SIBLINGS
|
|
# clear the OLDEST_SIBLING from all the siblings
|
|
for iface in self.ifaceobjdict[ifaceobj.name]:
|
|
iface.flags &= ~ifaceobj.OLDEST_SIBLING
|
|
# current sibling is the oldest
|
|
ifaceobj.flags |= ifaceobj.OLDEST_SIBLING
|
|
self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
|
|
|
|
def _keyword_text(self, value, validrange=None):
|
|
return isinstance(value, str) and len(value) > 0
|
|
|
|
def _keyword_mac(self, value, validrange=None):
|
|
if value.strip().startswith('ether'):
|
|
value = value.strip()[6:]
|
|
return re.match('[0-9a-f]{1,2}([-:])[0-9a-f]{1,2}(\\1[0-9a-f]{1,2}){4}$',
|
|
value.lower())
|
|
|
|
def _keyword_check_list(self, _list, obj, limit=None):
|
|
try:
|
|
if limit and limit > 0:
|
|
for i in range(0, limit):
|
|
obj(_list[i])
|
|
return len(_list) == limit
|
|
else:
|
|
for elem in _list:
|
|
obj(elem)
|
|
return True
|
|
except Exception as e:
|
|
self.logger.debug('keyword: check list: %s' % str(e))
|
|
return False
|
|
|
|
def _keyword_ipv4(self, value, validrange=None):
|
|
return self._keyword_check_list(value.split(), ipnetwork.IPv4Address, limit=1)
|
|
|
|
def _keyword_ipv4_prefixlen(self, value, validrange=None):
|
|
return self._keyword_check_list(value.split(), ipnetwork.IPv4Network, limit=1)
|
|
|
|
def _keyword_ipv6(self, value, validrange=None):
|
|
return self._keyword_check_list(value.split(), ipnetwork.IPv6Address, limit=1)
|
|
|
|
def _keyword_ipv6_prefixlen(self, value, validrange=None):
|
|
return self._keyword_check_list(value.split(), ipnetwork.IPv6Network, limit=1)
|
|
|
|
def _keyword_ip(self, value, validrange=None):
|
|
return self._keyword_check_list(value.split(), ipnetwork.IPAddress, limit=1)
|
|
|
|
def _keyword_ip_prefixlen(self, value, validrange=None):
|
|
return self._keyword_check_list(value.split(), ipnetwork.IPNetwork, limit=1)
|
|
|
|
def _keyword_mac_ip_prefixlen_list(self, value, validrange=None):
|
|
"""
|
|
MAC address followed by optional list of ip addresses
|
|
<mac> [<ip> <ip> ...]
|
|
ex: address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24
|
|
"""
|
|
try:
|
|
res = value.split()
|
|
if not self._keyword_mac(res[0]):
|
|
return False
|
|
for ip in res[1:]:
|
|
if not self._keyword_ip_prefixlen(ip):
|
|
return False
|
|
return True
|
|
except Exception as e:
|
|
self.logger.debug('keyword: mac ipaddr prefixlen: %s' % str(e))
|
|
return False
|
|
|
|
def _keyword_number_ipv4_list(self, value, validrange=None):
|
|
"""
|
|
<number>=<ipv4> [<number>=<ipv4> ...]
|
|
ex: bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1
|
|
"""
|
|
try:
|
|
elements = value.split(' ')
|
|
if not elements:
|
|
return False
|
|
for elem in elements:
|
|
v = elem.split('=')
|
|
int(v[0])
|
|
ipnetwork.IPv4Address(v[1])
|
|
return True
|
|
except Exception as e:
|
|
self.logger.debug('keyword: number ipv4: %s' % str(e))
|
|
return False
|
|
|
|
def _keyword_interface(self, ifacename, validrange=None):
|
|
return self.get_ifaceobjs(ifacename)
|
|
|
|
def _keyword_ipv4_vrf_text(self, value, validrange=None):
|
|
"""
|
|
<ipv4> "vrf" <text>
|
|
ex: clagd-backup-ip 10.10.10.42 vrf blue
|
|
"""
|
|
values = value.split()
|
|
size = len(values)
|
|
|
|
if size > 3 or size < 1:
|
|
return False
|
|
try:
|
|
ipnetwork.IPv4Address(values[0])
|
|
if size > 1:
|
|
if values[1] != 'vrf':
|
|
return False
|
|
if size > 2:
|
|
if not self._keyword_text(values[2]):
|
|
return False
|
|
return True
|
|
except Exception as e:
|
|
self.logger.debug('keyword: ipv4 vrf text: %s' % str(e))
|
|
return False
|
|
|
|
def _keyword_interface_list_with_value(self, value, validvals):
|
|
values = value.split()
|
|
try:
|
|
if len(values) == 1:
|
|
if values[0] in validvals:
|
|
return True
|
|
for v in values:
|
|
iface_value = v.split('=')
|
|
size = len(iface_value)
|
|
if size != 2:
|
|
if iface_value[0] == 'glob' or iface_value[0] == 'regex':
|
|
continue
|
|
return False
|
|
if not iface_value[1] in validvals:
|
|
return False
|
|
return True
|
|
except Exception as e:
|
|
self.logger.debug('keyword: interface list with value: %s' % str(e))
|
|
return False
|
|
|
|
def _keyword_interface_on_off_list(self, value, validrange=None):
|
|
"""
|
|
<yes|no> | ( <interface>=<on|off> [<interface>=<on|off> ...] )
|
|
ex: bridge-learning swp1=on swp2=off
|
|
"""
|
|
return self._keyword_interface_list_with_value(value, ['on', 'off'])
|
|
|
|
def _keyword_interface_yes_no_list(self, value, validrange=None):
|
|
"""
|
|
<yes|no> | ( <interface>=<yes|no> [<interface>=<yes|no> ...] )
|
|
ex: mstpctl-portrestrrole swp1=yes swp2=no
|
|
"""
|
|
return self._keyword_interface_list_with_value(value, ['yes', 'no'])
|
|
|
|
def _keyword_interface_yes_no_auto_list(self, value, validrange=None):
|
|
"""
|
|
<yes|no|auto> |
|
|
( <interface>=<yes|no|auto> [<interface>=<yes|no|auto> ...] )
|
|
ex: mstpctl-portp2p swp1=yes swp2=no swp3=auto
|
|
"""
|
|
return self._keyword_interface_list_with_value(value,
|
|
['yes', 'no', 'auto'])
|
|
|
|
def _keyword_interface_l2protocol_tunnel_list(self, value, validrange=None):
|
|
"""
|
|
bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all
|
|
bridge-l2protocol-tunnel lacp stp,lldp,cdp
|
|
bridge-l2protocol-tunnel stp lacp cdp
|
|
bridge-l2protocol-tunnel lldp pvst
|
|
bridge-l2protocol-tunnel stp
|
|
bridge-l2protocol-tunnel all
|
|
"""
|
|
try:
|
|
if '=' in value:
|
|
for intf_arg in value.split():
|
|
intf_arg_split = intf_arg.split('=')
|
|
for arg in intf_arg_split[1].replace(",", " ").split():
|
|
if arg not in ['all', 'stp', 'lldp', 'lacp', 'cdp', 'pvst']:
|
|
return False
|
|
else:
|
|
for arg in value.replace(",", " ").split():
|
|
if arg not in ['all', 'stp', 'lldp', 'lacp', 'cdp', 'pvst']:
|
|
return False
|
|
except Exception:
|
|
return False
|
|
return True
|
|
|
|
def _keyword_interface_yes_no_0_1_list(self, value, validrange=None):
|
|
"""
|
|
<yes|no|0|1> |
|
|
( <interface>=<yes|no|0|1> [<interface>=<yes|no|0|1> ...] )
|
|
ex: bridge-portmcrouter swp1=yes swp2=yes swp3=1
|
|
"""
|
|
return self._keyword_interface_list_with_value(value,
|
|
['yes', 'no', '1', '0', '2'])
|
|
|
|
def _keyword_interface_disabled_automatic_enabled_list(self, value, validrange=None):
|
|
return self._keyword_interface_list_with_value(value, [
|
|
'0', 'disabled', 'no',
|
|
'1', 'automatic', 'yes',
|
|
'2', 'enabled'])
|
|
|
|
def _keyword_interface_range_list_multiple_of_16(self, value, validrange):
|
|
return self._keyword_interface_range_list(value, validrange, multiple=16)
|
|
|
|
def _keyword_interface_range_list(self, value, validrange, multiple=None):
|
|
"""
|
|
<number> | ( <interface>=<number> [ <interface>=number> ...] )
|
|
ex: mstpctl-portpathcost swp1=0 swp2=1
|
|
"""
|
|
values = value.split()
|
|
try:
|
|
if len(values) == 1 and '=' not in values[0]:
|
|
try:
|
|
n = int(values[0])
|
|
if n < int(validrange[0]) or n > int(
|
|
validrange[1]):
|
|
raise invalidValueError('value of out range "%s":'
|
|
' valid attribute range: %s'
|
|
% (values[0],
|
|
'-'.join(validrange)))
|
|
|
|
if multiple is not None:
|
|
if not (n % multiple == 0):
|
|
raise invalidValueError('invalid value %s: must be a multiple of %s' % (n, multiple))
|
|
|
|
return True
|
|
except invalidValueError as e:
|
|
raise e
|
|
except Exception as e:
|
|
self.logger.debug('keyword: interface range list: %s'
|
|
% str(e))
|
|
return False
|
|
for v in values:
|
|
iface_value = v.split('=')
|
|
size = len(iface_value)
|
|
if size != 2:
|
|
return False
|
|
number = int(iface_value[1])
|
|
if number < int(validrange[0]) or number > int(
|
|
validrange[1]):
|
|
raise invalidValueError(
|
|
'value of out range "%s" for iface "%s":'
|
|
' valid attribute range: %s'
|
|
% (iface_value[1],
|
|
iface_value[0],
|
|
'-'.join(validrange)))
|
|
|
|
if multiple is not None:
|
|
if not (number % multiple == 0):
|
|
raise invalidValueError('invalid value %s: must be a multiple of %s' % (number, multiple))
|
|
|
|
return True
|
|
except invalidValueError as e:
|
|
raise e
|
|
except Exception as e:
|
|
self.logger.debug('keyword: interface range list: %s' % str(e))
|
|
return False
|
|
|
|
def _keyword_interface_list(self, value, validrange=None):
|
|
"""
|
|
[glob|regex] <interface> [ [glob|regex] <interface> ...]
|
|
ex: bridge-ports swp1 swp2 glob swp3-5.100 regex (swp[6|7|8].100)
|
|
"""
|
|
interface_list = value.split()
|
|
size = len(interface_list)
|
|
i = 0
|
|
while i < size:
|
|
if interface_list[i] == 'glob' or interface_list[i] == 'regex':
|
|
i += 1
|
|
else:
|
|
if not self._keyword_interface(interface_list[i]):
|
|
return False
|
|
i += 1
|
|
return True
|
|
|
|
def _keyword_number_range_list(self, value, validrange=None):
|
|
"""
|
|
<number> [<number>-<number>]
|
|
ex: bridge-vids 42 100-200
|
|
"""
|
|
number_list = value.split()
|
|
try:
|
|
i = 0
|
|
while i < len(number_list):
|
|
if '-' in number_list[i]:
|
|
range = number_list[i].split('-')
|
|
a = int(range[0])
|
|
b = int(range[1])
|
|
if a > b:
|
|
return False
|
|
else:
|
|
int(number_list[i])
|
|
i += 1
|
|
return True
|
|
except Exception as e:
|
|
self.logger.debug('keyword: number range list: %s' % str(e))
|
|
return False
|
|
|
|
def _keyword_number_interface_list(self, value, validrange=None):
|
|
"""
|
|
<number> <interface> [<interface>... [<number> <interface> ... ]]
|
|
bridge-waitport 42 swp1 swp2 swp3 9 swp4
|
|
"""
|
|
interface_list = value.split()
|
|
if not interface_list:
|
|
return False
|
|
try:
|
|
int(interface_list[0])
|
|
prev = True
|
|
for elem in interface_list[1:]:
|
|
try:
|
|
int(elem)
|
|
if prev:
|
|
return False
|
|
prev = True
|
|
except Exception:
|
|
prev = False
|
|
return not prev
|
|
except Exception as e:
|
|
self.logger.debug('keyword: number interface list: %s' % str(e))
|
|
return False
|
|
|
|
def _keyword_number(self, value, validrange=None):
|
|
try:
|
|
int_value = int(value)
|
|
if validrange is not None:
|
|
return int(validrange[0]) <= int_value <= int(validrange[1])
|
|
return True
|
|
except Exception as e:
|
|
self.logger.debug('keyword: number: %s' % str(e))
|
|
return False
|
|
|
|
def _is_keyword(self, value):
|
|
if isinstance(value, tuple):
|
|
return True
|
|
keyword_found = value in self.validate_keywords
|
|
if value.startswith('<') and value.endswith('>') and not keyword_found:
|
|
raise Exception('%s: invalid keyword, please make sure to use'
|
|
' a valid keyword see `ifquery -s`' % value)
|
|
return keyword_found
|
|
|
|
def _check_validvals_value(self, attrname, value, validvals, validrange):
|
|
if validvals and value not in validvals:
|
|
is_valid = False
|
|
for keyword in validvals:
|
|
if self._is_keyword(keyword):
|
|
if validrange:
|
|
if self.validate_keywords[keyword](value, validrange):
|
|
return {'result': True}
|
|
else:
|
|
if self.validate_keywords[keyword](value):
|
|
return {'result': True}
|
|
if not is_valid:
|
|
return {
|
|
'result': False,
|
|
'message': 'invalid value "%s": valid attribute values: %s'
|
|
% (value, validvals)
|
|
}
|
|
elif validvals and value in validvals:
|
|
pass
|
|
elif validrange:
|
|
if len(validrange) != 2:
|
|
raise Exception('%s: invalid range in addon configuration'
|
|
% '-'.join(validrange))
|
|
_value = int(value)
|
|
if _value < int(validrange[0]) or _value > int(validrange[1]):
|
|
return {
|
|
'result': False,
|
|
'message': 'value of out range "%s": '
|
|
'valid attribute range: %s'
|
|
% (value, '-'.join(validrange))
|
|
}
|
|
return {'result': True}
|
|
|
|
def _check_validvals(self, ifacename, module_name, attrs):
|
|
ifaceobj = self.get_ifaceobjs(ifacename)
|
|
if not ifaceobj:
|
|
return
|
|
success = True
|
|
for attrname, attrvalue in list(ifaceobj[0].config.items()):
|
|
try:
|
|
attrname_dict = attrs.get(attrname, {})
|
|
validvals = attrname_dict.get('validvals', [])
|
|
validrange = attrname_dict.get('validrange', [])
|
|
for value in attrvalue:
|
|
res = self._check_validvals_value(attrname,
|
|
value,
|
|
validvals,
|
|
validrange)
|
|
if not res['result']:
|
|
self.logger.warning('%s: %s: %s' %
|
|
(ifacename, attrname, res['message']))
|
|
success = False
|
|
except Exception as e:
|
|
self.logger.warning('addon \'%s\': %s: %s' % (module_name,
|
|
attrname,
|
|
str(e)))
|
|
success = False
|
|
return success
|
|
|
|
def _module_syntax_check(self, filtered_ifacenames):
|
|
result = True
|
|
for ifacename in filtered_ifacenames:
|
|
for module in list(self.modules.values()):
|
|
try:
|
|
if hasattr(module, '_modinfo'):
|
|
if not self._check_validvals(ifacename,
|
|
module.__class__.__name__,
|
|
module._modinfo.get('attrs', {})):
|
|
result = False
|
|
if hasattr(module, 'syntax_check') and callable(module.syntax_check):
|
|
if not module.syntax_check(self.get_ifaceobjs(ifacename)[0],
|
|
self.get_ifaceobjs):
|
|
result = False
|
|
except Exception as e:
|
|
self.logger.warning('%s: %s' % (ifacename, str(e)))
|
|
result = False
|
|
return result
|
|
|
|
def _iface_configattr_syntax_checker(self, attrname, attrval):
|
|
for m, mdict in list(self.module_attrs.items()):
|
|
if not mdict:
|
|
continue
|
|
attrsdict = mdict.get('attrs')
|
|
try:
|
|
a = attrsdict.get(attrname)
|
|
if a:
|
|
if a.get('deprecated'):
|
|
newa = a.get('new-attribute')
|
|
if newa:
|
|
self.logger.warning('attribute %s is deprecated. use %s instead.' %(attrname, newa))
|
|
else:
|
|
self.logger.warning('attribute %s is deprecated.'
|
|
%attrname)
|
|
return True
|
|
else:
|
|
for key in attrsdict:
|
|
if 'aliases' in attrsdict[key]:
|
|
if attrname in attrsdict[key]['aliases']:
|
|
return True
|
|
except AttributeError:
|
|
pass
|
|
return False
|
|
|
|
def _ifaceobj_syntax_checker(self, ifaceobj):
|
|
ret = True
|
|
for attrname, attrvalue in list(ifaceobj.config.items()):
|
|
found = False
|
|
for k, v in list(self.module_attrs.items()):
|
|
if v and v.get('attrs', {}).get(attrname):
|
|
found = True
|
|
break
|
|
if not found:
|
|
ret = False
|
|
self.logger.warning('%s: unsupported attribute \'%s\'' \
|
|
% (ifaceobj.name, attrname))
|
|
continue
|
|
return ret
|
|
|
|
def read_iface_config(self, raw=False):
|
|
""" Reads default network interface config /etc/network/interfaces. """
|
|
ret = True
|
|
nifaces = networkInterfaces(self.interfacesfile,
|
|
self.interfacesfileiobuf,
|
|
self.interfacesfileformat,
|
|
template_enable=self.config.get('template_enable', 0),
|
|
template_engine=self.config.get('template_engine'),
|
|
template_lookuppath=self.config.get('template_lookuppath'),
|
|
raw=raw)
|
|
if self._ifaceobj_squash or self._ifaceobj_squash_internal:
|
|
nifaces.subscribe('iface_found', self._save_iface_squash)
|
|
else:
|
|
nifaces.subscribe('iface_found', self._save_iface)
|
|
if self.config.get('addon_syntax_check', '1') == '1':
|
|
nifaces.subscribe('validateifaceattr',
|
|
self._iface_configattr_syntax_checker)
|
|
nifaces.subscribe('validateifaceobj', self._ifaceobj_syntax_checker)
|
|
nifaces.load()
|
|
if nifaces.errors or nifaces.warns:
|
|
ret = False
|
|
|
|
self._schedule_addon_translate()
|
|
return ret
|
|
|
|
def read_old_iface_config(self):
|
|
""" Reads the saved iface config instead of default iface config.
|
|
And saved iface config is already read by the statemanager """
|
|
self.ifaceobjdict = copy.deepcopy(self.statemanager.ifaceobjdict)
|
|
|
|
def _load_addon_modules_config(self):
|
|
""" Load addon modules config file """
|
|
|
|
with open(self.addon_modules_configfile, 'r') as f:
|
|
lines = f.readlines()
|
|
for l in lines:
|
|
try:
|
|
litems = l.strip(' \n\t\r').split(',')
|
|
if not litems or len(litems) < 2:
|
|
continue
|
|
operation = litems[0]
|
|
mname = litems[1]
|
|
self.module_ops[operation].append(mname)
|
|
except Exception as e:
|
|
self.logger.warning('error reading line \'%s\' %s:' %(l, str(e)))
|
|
continue
|
|
|
|
def load_addon_modules(self, modules_dir_list):
|
|
""" load python modules from modules_dir
|
|
|
|
Default modules_dir is /usr/share/ifupdownmodules
|
|
|
|
"""
|
|
failed_import = list()
|
|
|
|
self.logger.info('loading builtin modules from %s' % str(modules_dir_list))
|
|
self._load_addon_modules_config()
|
|
|
|
for modules_dir in modules_dir_list:
|
|
if not modules_dir in sys.path:
|
|
sys.path.insert(1, modules_dir)
|
|
try:
|
|
for op, mlist in list(self.module_ops.items()):
|
|
for mname in mlist:
|
|
if self.modules.get(mname):
|
|
continue
|
|
mpath = modules_dir + '/' + mname + '.py'
|
|
if os.path.exists(mpath) and mpath not in failed_import:
|
|
try:
|
|
m = __import__(mname)
|
|
mclass = getattr(m, mname)
|
|
except Exception as e:
|
|
self.logger.warning('cannot load "%s" module: %s' % (mname, str(e)))
|
|
failed_import.append(mpath)
|
|
continue
|
|
try:
|
|
minstance = mclass()
|
|
script_override = minstance.get_overrides_ifupdown_scripts()
|
|
self.overridden_ifupdown_scripts.extend(script_override)
|
|
except moduleNotSupported as e:
|
|
self.logger.info('module %s not loaded (%s)'
|
|
%(mname, str(e)))
|
|
continue
|
|
except Exception:
|
|
raise
|
|
self.modules[mname] = minstance
|
|
try:
|
|
self.module_attrs[mname] = minstance.get_modinfo()
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
raise
|
|
|
|
# Assign all modules to query operations
|
|
self.module_ops['query-checkcurr'] = list(self.modules.keys())
|
|
self.module_ops['query-running'] = list(self.modules.keys())
|
|
self.module_ops['query-dependency'] = list(self.modules.keys())
|
|
self.module_ops['query'] = list(self.modules.keys())
|
|
self.module_ops['query-raw'] = list(self.modules.keys())
|
|
|
|
def _keyword_number_comma_range_list(self, value, validrange=None):
|
|
return self._keyword_number_range_list(value.replace(',', ' '), validrange=validrange)
|
|
|
|
|
|
def _modules_help(self, fmt):
|
|
""" Prints addon modules supported syntax """
|
|
|
|
if fmt == 'json':
|
|
modinfos = {}
|
|
for key, value in list(self.modules.items()):
|
|
if hasattr(value, '_modinfo'):
|
|
modinfos[key] = {
|
|
'mhelp': value._modinfo['mhelp'],
|
|
'attrs': value.merge_modinfo_with_policy_files()
|
|
}
|
|
print(json.dumps(modinfos))
|
|
else:
|
|
indent = ' '
|
|
for m, mdict in list(self.module_attrs.items()):
|
|
if not mdict:
|
|
continue
|
|
print(('%s: %s' %(m, mdict.get('mhelp'))))
|
|
attrdict = self.modules[m].merge_modinfo_with_policy_files()
|
|
if not attrdict:
|
|
continue
|
|
try:
|
|
for attrname, attrvaldict in list(attrdict.items()):
|
|
if attrvaldict.get('compat', False):
|
|
continue
|
|
print(('%s%s' %(indent, attrname)))
|
|
print(('%shelp: %s' %(indent + ' ',
|
|
attrvaldict.get('help', ''))))
|
|
print(('%srequired: %s' %(indent + ' ',
|
|
attrvaldict.get('required', False))))
|
|
default = attrvaldict.get('default')
|
|
if default:
|
|
print(('%sdefault: %s' %(indent + ' ', default)))
|
|
|
|
validrange = attrvaldict.get('validrange')
|
|
if validrange:
|
|
print(('%svalidrange: %s-%s'
|
|
%(indent + ' ', validrange[0], validrange[1])))
|
|
|
|
validvals = attrvaldict.get('validvals')
|
|
if validvals:
|
|
print(('%svalidvals: %s'
|
|
%(indent + ' ', ','.join(validvals))))
|
|
|
|
examples = attrvaldict.get('example')
|
|
if not examples:
|
|
continue
|
|
|
|
print('%sexample:' %(indent + ' '))
|
|
for e in examples:
|
|
print('%s%s' %(indent + ' ', e))
|
|
except Exception:
|
|
pass
|
|
print('')
|
|
|
|
def load_scripts(self, modules_dir):
|
|
""" loading user modules from /etc/network/.
|
|
|
|
Note that previously loaded python modules override modules found
|
|
under /etc/network if any
|
|
|
|
"""
|
|
|
|
self.logger.info('looking for user scripts under %s' %modules_dir)
|
|
for op, mlist in list(self.script_ops.items()):
|
|
msubdir = modules_dir + '/if-%s.d' %op
|
|
self.logger.info('loading scripts under %s ...' %msubdir)
|
|
try:
|
|
module_list = os.listdir(msubdir)
|
|
for module in module_list:
|
|
if self.modules.get(module) or module in self.overridden_ifupdown_scripts:
|
|
continue
|
|
self.script_ops[op].append(msubdir + '/' + module)
|
|
except Exception:
|
|
# continue reading
|
|
pass
|
|
|
|
|
|
def _schedule_addon_translate(self):
|
|
merged_ifaceobjs = list(itertools.chain.from_iterable(self.ifaceobjdict.values()))
|
|
|
|
for addon in self.modules.values():
|
|
try:
|
|
addon.translate(merged_ifaceobjs)
|
|
except AttributeError:
|
|
pass
|
|
|
|
def _sched_ifaces(self, ifacenames, ops, skipupperifaces=False,
|
|
followdependents=True, sort=False):
|
|
self.logger.debug('scheduling \'%s\' for %s'
|
|
%(str(ops), str(ifacenames)))
|
|
self._pretty_print_ordered_dict('dependency graph',
|
|
self.dependency_graph)
|
|
ifaceScheduler.sched_ifaces(self, ifacenames, ops,
|
|
dependency_graph=self.dependency_graph,
|
|
order=ifaceSchedulerFlags.INORDER
|
|
if 'down' in ops[0]
|
|
else ifaceSchedulerFlags.POSTORDER,
|
|
followdependents=followdependents,
|
|
skipupperifaces=skipupperifaces,
|
|
sort=True if (sort or ifupdownflags.flags.CLASS) else False)
|
|
return ifaceScheduler.get_sched_status()
|
|
|
|
def _render_ifacename(self, ifacename):
|
|
new_ifacenames = []
|
|
vlan_match = re.match("^([\d]+)-([\d]+)", ifacename)
|
|
if vlan_match:
|
|
vlan_groups = vlan_match.groups()
|
|
if vlan_groups[0] and vlan_groups[1]:
|
|
[new_ifacenames.append('%d' %v)
|
|
for v in range(int(vlan_groups[0]),
|
|
int(vlan_groups[1])+1)]
|
|
return new_ifacenames
|
|
|
|
def _preprocess_ifacenames(self, ifacenames):
|
|
""" validates interface list for config existance.
|
|
|
|
returns -1 if one or more interface not found. else, returns 0
|
|
|
|
"""
|
|
new_ifacenames = []
|
|
err_iface = ''
|
|
for i in ifacenames:
|
|
ifaceobjs = self.get_ifaceobjs(i)
|
|
if not ifaceobjs:
|
|
# if name not available, render interface name and check again
|
|
rendered_ifacenames = utils.expand_iface_range(i)
|
|
if rendered_ifacenames:
|
|
for ri in rendered_ifacenames:
|
|
ifaceobjs = self.get_ifaceobjs(ri)
|
|
if not ifaceobjs:
|
|
err_iface += ' ' + ri
|
|
else:
|
|
new_ifacenames.append(ri)
|
|
else:
|
|
err_iface += ' ' + i
|
|
else:
|
|
new_ifacenames.append(i)
|
|
if err_iface:
|
|
raise Exception('cannot find interfaces:%s' %err_iface)
|
|
return new_ifacenames
|
|
|
|
def _iface_whitelisted(self, auto, allow_classes, excludepats, ifacename):
|
|
""" Checks if interface is whitelisted depending on set of parameters.
|
|
|
|
interfaces are checked against the allow_classes and auto lists.
|
|
|
|
"""
|
|
|
|
ret = True
|
|
|
|
# Check if interface matches the exclude patter
|
|
if excludepats:
|
|
for e in excludepats:
|
|
if re.search(e, ifacename):
|
|
ret = False
|
|
ifaceobjs = self.get_ifaceobjs(ifacename)
|
|
if not ifaceobjs:
|
|
if ret:
|
|
self.logger.debug('iface %s' %ifacename + ' not found')
|
|
return ret
|
|
# If matched exclude pattern, return false
|
|
if not ret:
|
|
for i in ifaceobjs:
|
|
i.blacklisted = True
|
|
self.blacklisted_ifaces_present = True
|
|
return ret
|
|
# Check if interface belongs to the class
|
|
# the user is interested in, if not return false
|
|
if allow_classes:
|
|
ret = False
|
|
for i in ifaceobjs:
|
|
if i.classes:
|
|
common = set(allow_classes).intersection(set(i.classes))
|
|
if common:
|
|
ret = True
|
|
if not ret:
|
|
# If a class was requested and interface does not belong
|
|
# to the class, only then mark the ifaceobjs as blacklisted
|
|
self.blacklisted_ifaces_present = True
|
|
for i in ifaceobjs:
|
|
i.blacklisted = True
|
|
return ret
|
|
# If the user has requested auto class, check if the interface
|
|
# is marked auto
|
|
if auto:
|
|
ret = False
|
|
for i in ifaceobjs:
|
|
if i.auto:
|
|
ret = True
|
|
if not ret:
|
|
# If auto was requested and interface was not marked auto,
|
|
# only then mark all of them as blacklisted
|
|
self.blacklisted_ifaces_present = True
|
|
for i in ifaceobjs:
|
|
i.blacklisted = True
|
|
return ret
|
|
|
|
def _compat_conv_op_to_mode(self, op):
|
|
""" Returns old op name to work with existing scripts """
|
|
if 'up' in op:
|
|
return 'start'
|
|
elif 'down' in op:
|
|
return 'stop'
|
|
else:
|
|
return op
|
|
|
|
def generate_running_env(self, ifaceobj, op):
|
|
""" Generates a dictionary with env variables required for
|
|
an interface. Used to support script execution for interfaces.
|
|
"""
|
|
|
|
cenv = None
|
|
iface_env = ifaceobj.get_env()
|
|
if iface_env:
|
|
cenv = dict(os.environ)
|
|
if cenv:
|
|
cenv.update(iface_env)
|
|
else:
|
|
cenv = iface_env
|
|
else:
|
|
cenv = {}
|
|
cenv['MODE'] = self._compat_conv_op_to_mode(op)
|
|
cenv['PHASE'] = op
|
|
|
|
return cenv
|
|
|
|
def _save_state(self):
|
|
if (not self.flags.STATEMANAGER_ENABLE or
|
|
not self.flags.STATEMANAGER_UPDATE):
|
|
return
|
|
try:
|
|
# Update persistant iface states
|
|
self.statemanager.save_state()
|
|
except Exception as e:
|
|
if self.logger.isEnabledFor(logging.DEBUG):
|
|
t = sys.exc_info()[2]
|
|
traceback.print_tb(t)
|
|
self.logger.warning('error saving state (%s)' %str(e))
|
|
|
|
def set_type(self, type):
|
|
if type == 'iface':
|
|
self.type = ifaceType.IFACE
|
|
elif type == 'vlan':
|
|
self.type = ifaceType.BRIDGE_VLAN
|
|
else:
|
|
self.type = ifaceType.UNKNOWN
|
|
|
|
def _process_delay_admin_state_queue(self, op):
|
|
if not self._delay_admin_state_iface_queue:
|
|
return
|
|
if op == 'up':
|
|
func = self.netlink.link_up
|
|
elif op == 'down':
|
|
func = self.netlink.link_down
|
|
else:
|
|
return
|
|
for i in self._delay_admin_state_iface_queue:
|
|
try:
|
|
if self.link_exists(i):
|
|
func(i)
|
|
except Exception as e:
|
|
self.logger.warning(str(e))
|
|
pass
|
|
|
|
def _get_iface_exclude_companion(self, ifacename):
|
|
try:
|
|
return ifupdown2.ifupdown.policymanager.policymanager_api.get_iface_default(
|
|
module_name='main', ifname=ifacename,
|
|
attr='exclude-companion')
|
|
except Exception:
|
|
return ifupdown.policymanager.policymanager_api.get_iface_default(
|
|
module_name='main', ifname=ifacename,
|
|
attr='exclude-companion')
|
|
|
|
def _preprocess_excludepats(self, excludepats):
|
|
new_excludepats = excludepats
|
|
for e in excludepats:
|
|
ifaceobjs = self.get_ifaceobjs(e)
|
|
for iobj in ifaceobjs or []:
|
|
ec = iobj.get_attr_value_first('exclude-companion')
|
|
if not ec:
|
|
ec = self._get_iface_exclude_companion(e)
|
|
if not ec:
|
|
continue
|
|
else:
|
|
for ee in ec.split():
|
|
if ee in new_excludepats:
|
|
continue
|
|
if self.get_ifaceobjs(ee):
|
|
# if we know the object add it to the new
|
|
# excludepats list
|
|
new_excludepats.append(ee)
|
|
self.logger.info('excludepats after processing companions [%s]' %' '.join(new_excludepats))
|
|
return new_excludepats
|
|
|
|
def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
|
|
excludepats=None, printdependency=None, syntaxcheck=False,
|
|
type=None, skipupperifaces=False):
|
|
"""This brings the interface(s) up
|
|
|
|
Args:
|
|
ops (list): list of ops to perform on the interface(s).
|
|
Eg: ['pre-up', 'up', 'post-up'
|
|
|
|
Kwargs:
|
|
auto (bool): act on interfaces marked auto
|
|
allow_classes (list): act on interfaces belonging to classes in the list
|
|
ifacenames (list): act on interfaces specified in this list
|
|
excludepats (list): list of patterns of interfaces to exclude
|
|
syntaxcheck (bool): only perform syntax check
|
|
"""
|
|
|
|
self.set_type(type)
|
|
|
|
if allow_classes:
|
|
ifupdownflags.flags.CLASS = True
|
|
if not self.flags.ADDONS_ENABLE:
|
|
self.flags.STATEMANAGER_UPDATE = False
|
|
if auto:
|
|
ifupdownflags.flags.ALL = True
|
|
ifupdownflags.flags.WITH_DEPENDS = True
|
|
try:
|
|
iface_read_ret = self.read_iface_config()
|
|
except Exception:
|
|
raise
|
|
|
|
if excludepats:
|
|
excludepats = self._preprocess_excludepats(excludepats)
|
|
|
|
filtered_ifacenames = None
|
|
if ifacenames:
|
|
ifacenames = self._preprocess_ifacenames(ifacenames)
|
|
|
|
if allow_classes:
|
|
filtered_ifacenames = self._get_filtered_ifacenames_with_classes(auto, allow_classes, excludepats, ifacenames)
|
|
|
|
# if iface list not given by user, assume all from config file
|
|
if not ifacenames: ifacenames = list(self.ifaceobjdict.keys())
|
|
|
|
if not filtered_ifacenames:
|
|
# filter interfaces based on auto and allow classes
|
|
filtered_ifacenames = [i for i in ifacenames
|
|
if self._iface_whitelisted(auto, allow_classes,
|
|
excludepats, i)]
|
|
|
|
if not filtered_ifacenames:
|
|
raise Exception('no ifaces found matching given allow lists')
|
|
|
|
if printdependency:
|
|
self.populate_dependency_info(ops, filtered_ifacenames)
|
|
self.print_dependency(filtered_ifacenames, printdependency)
|
|
return
|
|
else:
|
|
self.populate_dependency_info(ops)
|
|
|
|
# If only syntax check was requested, return here.
|
|
# return here because we want to make sure most
|
|
# errors above are caught and reported.
|
|
if syntaxcheck:
|
|
if not self._module_syntax_check(filtered_ifacenames):
|
|
raise Exception()
|
|
if not iface_read_ret:
|
|
raise Exception()
|
|
elif self._any_iface_errors(filtered_ifacenames):
|
|
raise Exception()
|
|
return
|
|
|
|
ret = None
|
|
try:
|
|
ret = self._sched_ifaces(filtered_ifacenames, ops,
|
|
skipupperifaces=skipupperifaces,
|
|
followdependents=True
|
|
if ifupdownflags.flags.WITH_DEPENDS
|
|
else False)
|
|
finally:
|
|
self._process_delay_admin_state_queue('up')
|
|
if not ifupdownflags.flags.DRYRUN and self.flags.ADDONS_ENABLE:
|
|
self._save_state()
|
|
|
|
if not iface_read_ret or not ret:
|
|
raise Exception()
|
|
|
|
self.check_running_configuration(filtered_ifacenames)
|
|
|
|
def check_running_configuration(self, filtered_ifacenames, all=False):
|
|
"""
|
|
Print warning and better info message when we don't recognize an interface
|
|
AKA when the interface wasn't created.
|
|
|
|
:param filtered_ifacenames:
|
|
:param all:
|
|
:return:
|
|
"""
|
|
if not filtered_ifacenames:
|
|
filtered_ifacenames = []
|
|
|
|
for ifname, ifaceobj_list in self.ifaceobjdict.items():
|
|
|
|
if not all and ifname not in filtered_ifacenames:
|
|
continue
|
|
|
|
auto = True
|
|
|
|
for ifaceobj in ifaceobj_list:
|
|
if not ifaceobj.auto:
|
|
auto = False
|
|
break
|
|
|
|
if not ifupdownflags.flags.DRYRUN and auto and not os.path.exists("/sys/class/net/%s" % ifname) and not self._is_ifaceobj_bridge_vlan(ifaceobj_list):
|
|
self.logger.warning("%s: interface not recognized - please check interface configuration" % ifname)
|
|
|
|
def _is_ifaceobj_bridge_vlan(self, ifaceobj_list):
|
|
for ifaceobj in ifaceobj_list:
|
|
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
|
|
return True
|
|
return False
|
|
|
|
def _get_filtered_ifacenames_with_classes(self, auto, allow_classes, excludepats, ifacenames):
|
|
# if user has specified ifacelist and allow_classes
|
|
# append the allow_classes interfaces to user
|
|
# ifacelist
|
|
filtered_ifacenames = [i for i in list(self.ifaceobjdict.keys())
|
|
if self._iface_whitelisted(auto, allow_classes,
|
|
excludepats, i)]
|
|
filtered_ifacenames += ifacenames
|
|
|
|
for intf in ifacenames:
|
|
for obj in self.get_ifaceobjs(intf) or []:
|
|
obj.blacklisted = False
|
|
|
|
return filtered_ifacenames
|
|
|
|
def down(self, ops, auto=False, allow_classes=None, ifacenames=None,
|
|
excludepats=None, printdependency=None, usecurrentconfig=False,
|
|
type=None):
|
|
""" down an interface """
|
|
|
|
self.set_type(type)
|
|
|
|
if allow_classes:
|
|
ifupdownflags.flags.CLASS = True
|
|
if not self.flags.ADDONS_ENABLE:
|
|
self.flags.STATEMANAGER_UPDATE = False
|
|
if auto:
|
|
ifupdownflags.flags.ALL = True
|
|
ifupdownflags.flags.WITH_DEPENDS = True
|
|
# For down we need to look at old state, unless usecurrentconfig
|
|
# is set
|
|
if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE and
|
|
self.statemanager.ifaceobjdict):
|
|
# Since we are using state manager objects,
|
|
# skip the updating of state manager objects
|
|
self.logger.debug('Looking at old state ..')
|
|
self.read_old_iface_config()
|
|
else:
|
|
# If no old state available
|
|
try:
|
|
self.read_iface_config()
|
|
except Exception as e:
|
|
raise Exception('error reading iface config (%s)' %str(e))
|
|
|
|
if excludepats:
|
|
excludepats = self._preprocess_excludepats(excludepats)
|
|
|
|
filtered_ifacenames = None
|
|
if ifacenames:
|
|
# If iface list is given by the caller, always check if iface
|
|
# is present
|
|
try:
|
|
ifacenames = self._preprocess_ifacenames(ifacenames)
|
|
|
|
if allow_classes:
|
|
filtered_ifacenames = self._get_filtered_ifacenames_with_classes(auto, allow_classes, excludepats, ifacenames)
|
|
|
|
except Exception as e:
|
|
raise Exception('%s' %str(e) +
|
|
' (interface was probably never up ?)')
|
|
|
|
|
|
# if iface list not given by user, assume all from config file
|
|
if not ifacenames: ifacenames = list(self.ifaceobjdict.keys())
|
|
|
|
if not filtered_ifacenames:
|
|
# filter interfaces based on auto and allow classes
|
|
filtered_ifacenames = [i for i in ifacenames
|
|
if self._iface_whitelisted(auto, allow_classes,
|
|
excludepats, i)]
|
|
|
|
if not filtered_ifacenames:
|
|
raise Exception('no ifaces found matching given allow lists ' +
|
|
'(or interfaces were probably never up ?)')
|
|
|
|
if printdependency:
|
|
self.populate_dependency_info(ops, filtered_ifacenames)
|
|
self.print_dependency(filtered_ifacenames, printdependency)
|
|
return
|
|
else:
|
|
self.populate_dependency_info(ops)
|
|
|
|
try:
|
|
self._sched_ifaces(filtered_ifacenames, ops,
|
|
followdependents=True
|
|
if ifupdownflags.flags.WITH_DEPENDS else False)
|
|
finally:
|
|
self._process_delay_admin_state_queue('down')
|
|
if not ifupdownflags.flags.DRYRUN and self.flags.ADDONS_ENABLE:
|
|
self._save_state()
|
|
|
|
def query(self, ops, auto=False, format_list=False, allow_classes=None,
|
|
ifacenames=None,
|
|
excludepats=None, printdependency=None,
|
|
format='native', type=None):
|
|
""" query an interface """
|
|
|
|
self.set_type(type)
|
|
|
|
# Let us forget internal squashing when it comes to
|
|
# ifquery. It can surprise people relying of ifquery
|
|
# output
|
|
self._ifaceobj_squash_internal = False
|
|
|
|
if allow_classes:
|
|
ifupdownflags.flags.CLASS = True
|
|
if self.flags.STATEMANAGER_ENABLE and ops[0] == 'query-savedstate':
|
|
return self.statemanager.dump_pretty(ifacenames)
|
|
self.flags.STATEMANAGER_UPDATE = False
|
|
|
|
iface_read_ret = True
|
|
|
|
if auto:
|
|
self.logger.debug('setting flag ALL')
|
|
ifupdownflags.flags.ALL = True
|
|
ifupdownflags.flags.WITH_DEPENDS = True
|
|
|
|
if ops[0] == 'query-syntax':
|
|
self._modules_help(format)
|
|
return
|
|
elif ops[0] == 'query-running':
|
|
# create fake devices to all dependents that dont have config
|
|
for i in ifacenames:
|
|
self.create_n_save_ifaceobj(i, ifacePrivFlags(False, True))
|
|
else:
|
|
try:
|
|
iface_read_ret = self.read_iface_config(raw=ops[0] == "query-raw")
|
|
except Exception:
|
|
raise
|
|
|
|
if ifacenames and ops[0] != 'query-running':
|
|
# If iface list is given, always check if iface is present
|
|
ifacenames = self._preprocess_ifacenames(ifacenames)
|
|
|
|
if allow_classes:
|
|
filtered_ifacenames = self._get_filtered_ifacenames_with_classes(auto, allow_classes, excludepats, ifacenames)
|
|
|
|
# if iface list not given by user, assume all from config file
|
|
if not ifacenames: ifacenames = list(self.ifaceobjdict.keys())
|
|
|
|
# filter interfaces based on auto and allow classes
|
|
if ops[0] == 'query-running':
|
|
filtered_ifacenames = ifacenames
|
|
elif not allow_classes:
|
|
filtered_ifacenames = [
|
|
i for i in ifacenames
|
|
if self._iface_whitelisted(
|
|
auto,
|
|
allow_classes,
|
|
excludepats, i
|
|
)
|
|
]
|
|
|
|
if not filtered_ifacenames:
|
|
raise Exception('no ifaces found matching ' +
|
|
'given allow lists')
|
|
|
|
self.populate_dependency_info(ops)
|
|
if ops[0] == 'query-dependency' and printdependency:
|
|
self.print_dependency(filtered_ifacenames, printdependency)
|
|
return
|
|
|
|
if format_list and (ops[0] == 'query' or ops[0] == 'query-raw'):
|
|
return self.print_ifaceobjs_list(filtered_ifacenames)
|
|
|
|
if ops[0] == 'query' and not ifupdownflags.flags.WITHDEFAULTS:
|
|
return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
|
|
elif ops[0] == 'query-raw':
|
|
return self.print_ifaceobjs_raw(filtered_ifacenames, format)
|
|
|
|
ret = self._sched_ifaces(filtered_ifacenames, ops,
|
|
followdependents=True
|
|
if ifupdownflags.flags.WITH_DEPENDS else False)
|
|
|
|
if ops[0] == 'query' and ifupdownflags.flags.WITHDEFAULTS:
|
|
return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
|
|
elif ops[0] == 'query-checkcurr':
|
|
if self.print_ifaceobjscurr_pretty(filtered_ifacenames, format):
|
|
# if any of the object has an error, signal that silently
|
|
raise Exception('')
|
|
elif ops[0] == 'query-running':
|
|
self.print_ifaceobjsrunning_pretty(filtered_ifacenames, format)
|
|
return
|
|
|
|
if not iface_read_ret or not ret:
|
|
raise Exception()
|
|
|
|
def _reload_currentlyup(self, upops, downops, auto=False, allow=None,
|
|
ifacenames=None, excludepats=None, usecurrentconfig=False,
|
|
syntaxcheck=False, **extra_args):
|
|
""" reload currently up interfaces """
|
|
new_ifaceobjdict = {}
|
|
|
|
self.logger.info('reloading interfaces that are currently up ..')
|
|
|
|
try:
|
|
iface_read_ret = self.read_iface_config()
|
|
except Exception:
|
|
raise
|
|
if not self.ifaceobjdict:
|
|
self.logger.warning("nothing to reload ..exiting.")
|
|
return
|
|
already_up_ifacenames = []
|
|
if not ifacenames: ifacenames = list(self.ifaceobjdict.keys())
|
|
|
|
if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE
|
|
and self.statemanager.ifaceobjdict):
|
|
already_up_ifacenames = list(self.statemanager.ifaceobjdict.keys())
|
|
|
|
# Get already up interfaces that still exist in the interfaces file
|
|
already_up_ifacenames_not_present = set(
|
|
already_up_ifacenames).difference(ifacenames)
|
|
already_up_ifacenames_still_present = set(
|
|
already_up_ifacenames).difference(
|
|
already_up_ifacenames_not_present)
|
|
|
|
interfaces_to_up = already_up_ifacenames_still_present
|
|
|
|
# generate dependency graph of interfaces
|
|
self.populate_dependency_info(upops, interfaces_to_up)
|
|
|
|
# If only syntax check was requested, return here.
|
|
# return here because we want to make sure most
|
|
# errors above are caught and reported.
|
|
if syntaxcheck:
|
|
if not self._module_syntax_check(interfaces_to_up):
|
|
raise Exception()
|
|
if not iface_read_ret:
|
|
raise Exception()
|
|
elif self._any_iface_errors(interfaces_to_up):
|
|
raise Exception()
|
|
return
|
|
|
|
if (already_up_ifacenames_not_present and
|
|
self.config.get('ifreload_currentlyup_down_notpresent') == '1'):
|
|
self.logger.info('reload: schedule down on interfaces: %s'
|
|
%str(already_up_ifacenames_not_present))
|
|
|
|
# Save a copy of new iface objects and dependency_graph
|
|
new_ifaceobjdict = dict(self.ifaceobjdict)
|
|
new_dependency_graph = dict(self.dependency_graph)
|
|
|
|
# old interface config is read into self.ifaceobjdict
|
|
self.read_old_iface_config()
|
|
|
|
# reinitialize dependency graph
|
|
self.dependency_graph = OrderedDict({})
|
|
falready_up_ifacenames_not_present = [i for i in
|
|
already_up_ifacenames_not_present
|
|
if self._iface_whitelisted(auto, allow,
|
|
excludepats, i)]
|
|
self.populate_dependency_info(downops,
|
|
falready_up_ifacenames_not_present)
|
|
self._sched_ifaces(falready_up_ifacenames_not_present, downops,
|
|
followdependents=False, sort=True)
|
|
else:
|
|
self.logger.info('no interfaces to down ..')
|
|
|
|
# Now, run 'up' with new config dict
|
|
# reset statemanager update flag to default
|
|
if auto:
|
|
ifupdownflags.flags.ALL = True
|
|
ifupdownflags.flags.WITH_DEPENDS = True
|
|
if new_ifaceobjdict:
|
|
# and now, ifaceobjdict is back to current config
|
|
self.ifaceobjdict = new_ifaceobjdict
|
|
self.dependency_graph = new_dependency_graph
|
|
|
|
if not self.ifaceobjdict:
|
|
self.logger.info('no interfaces to up')
|
|
return
|
|
self.logger.info('reload: scheduling up on interfaces: %s'
|
|
%str(interfaces_to_up))
|
|
ret = self._sched_ifaces(interfaces_to_up, upops,
|
|
followdependents=True
|
|
if ifupdownflags.flags.WITH_DEPENDS else False)
|
|
if ifupdownflags.flags.DRYRUN:
|
|
return
|
|
self._save_state()
|
|
|
|
if not iface_read_ret or not ret:
|
|
raise Exception()
|
|
|
|
def _reload_default(self, upops, downops, auto=False, allow=None,
|
|
ifacenames=None, excludepats=None, usecurrentconfig=False,
|
|
syntaxcheck=False, **extra_args):
|
|
""" reload interface config """
|
|
new_ifaceobjdict = {}
|
|
|
|
try:
|
|
iface_read_ret = self.read_iface_config()
|
|
except Exception:
|
|
raise
|
|
|
|
if not self.ifaceobjdict:
|
|
self.logger.warning("nothing to reload ..exiting.")
|
|
return
|
|
|
|
if not ifacenames: ifacenames = list(self.ifaceobjdict.keys())
|
|
new_filtered_ifacenames = [i for i in ifacenames
|
|
if self._iface_whitelisted(auto, allow,
|
|
excludepats, i)]
|
|
# generate dependency graph of interfaces
|
|
self.populate_dependency_info(upops)
|
|
|
|
# If only syntax check was requested, return here.
|
|
# return here because we want to make sure most
|
|
# errors above are caught and reported.
|
|
if syntaxcheck:
|
|
if not self._module_syntax_check(new_filtered_ifacenames):
|
|
raise Exception()
|
|
if not iface_read_ret:
|
|
raise Exception()
|
|
elif self._any_iface_errors(new_filtered_ifacenames):
|
|
raise Exception()
|
|
return
|
|
|
|
if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE
|
|
and self.statemanager.ifaceobjdict):
|
|
# Save a copy of new iface objects and dependency_graph
|
|
new_ifaceobjdict = dict(self.ifaceobjdict)
|
|
new_dependency_graph = dict(self.dependency_graph)
|
|
|
|
self.ifaceobjdict = OrderedDict({})
|
|
self.dependency_graph = OrderedDict({})
|
|
|
|
# if old state is present, read old state and mark op for 'down'
|
|
# followed by 'up' aka: reload
|
|
# old interface config is read into self.ifaceobjdict
|
|
self.read_old_iface_config()
|
|
op = 'reload'
|
|
else:
|
|
# oldconfig not available, continue with 'up' with new config
|
|
op = 'up'
|
|
new_ifaceobjdict = self.ifaceobjdict
|
|
new_dependency_graph = self.dependency_graph
|
|
|
|
if excludepats:
|
|
excludepats = self._preprocess_excludepats(excludepats)
|
|
|
|
if op == 'reload' and ifacenames:
|
|
ifacenames = list(self.ifaceobjdict.keys())
|
|
old_filtered_ifacenames = [i for i in ifacenames
|
|
if self._iface_whitelisted(auto, allow,
|
|
excludepats, i)]
|
|
|
|
# generate dependency graph of old interfaces,
|
|
# This should make sure built in interfaces are
|
|
# populated. disable check shared dependents as an optimization.
|
|
# these are saved interfaces and dependency for these
|
|
# have been checked before they became part of saved state.
|
|
try:
|
|
self.flags.CHECK_SHARED_DEPENDENTS = False
|
|
self.populate_dependency_info(upops, old_ifaceobjs=True)
|
|
self.flags.CHECK_SHARED_DEPENDENTS = True
|
|
except Exception as e:
|
|
self.logger.info("error generating dependency graph for "
|
|
"saved interfaces (%s)" %str(e))
|
|
pass
|
|
|
|
# make sure we pick up built-in interfaces
|
|
# if config file had 'ifreload_down_changed' variable
|
|
# set, also look for interfaces that changed to down them
|
|
down_changed = int(self.config.get('ifreload_down_changed', '1'))
|
|
|
|
# Generate the interface down list
|
|
# Interfaces that go into the down list:
|
|
# - interfaces that were present in last config and are not
|
|
# present in the new config
|
|
# - interfaces that were changed between the last and current
|
|
# config
|
|
ifacedownlist = []
|
|
for ifname in list(self.ifaceobjdict.keys()):
|
|
lastifaceobjlist = self.ifaceobjdict.get(ifname)
|
|
if not self.is_ifaceobj_builtin(lastifaceobjlist[0]):
|
|
# if interface is not built-in and is not in
|
|
# old filtered ifacenames
|
|
if ifname not in old_filtered_ifacenames:
|
|
continue
|
|
objidx = 0
|
|
# If interface is not present in the new file
|
|
# append it to the down list
|
|
newifaceobjlist = new_ifaceobjdict.get(ifname)
|
|
if not newifaceobjlist:
|
|
ifacedownlist.append(ifname)
|
|
continue
|
|
# If ifaceobj was present in the old interfaces file,
|
|
# and does not have a config in the new interfaces file
|
|
# but has been picked up as a dependent of another
|
|
# interface, catch it here. This catches a common error
|
|
# for example: remove a bond section from the interfaces
|
|
# file, but leave it around as a bridge port
|
|
# XXX: Ideally its better to just add it to the
|
|
# ifacedownlist. But we will be cautious here
|
|
# and just print a warning
|
|
if (self.is_ifaceobj_noconfig(newifaceobjlist[0]) and
|
|
not self.is_ifaceobj_builtin(newifaceobjlist[0]) and
|
|
lastifaceobjlist[0].is_config_present() and
|
|
lastifaceobjlist[0].link_kind):
|
|
|
|
# Check if interface is picked up by a regex in the upperifaces.
|
|
print_warning = True
|
|
|
|
for upper in newifaceobjlist[objidx].upperifaces or []:
|
|
slaves = []
|
|
for upper_ifaceobj in self.ifaceobjdict.get(upper):
|
|
slaves.extend(upper_ifaceobj.get_attr_value("bond-slaves") or [])
|
|
slaves.extend(upper_ifaceobj.get_attr_value("bridge-ports") or [])
|
|
slaves_string = " ".join(slaves)
|
|
if newifaceobjlist[objidx].name not in slaves_string:
|
|
print_warning = "regex" not in slaves_string
|
|
if not print_warning:
|
|
break
|
|
###############################################################
|
|
|
|
warning_no_config_regex = (
|
|
"%s: misconfig ? removed but still exists as a dependency of %s.\n"
|
|
"Please remove the dependency manually `ifdown %s` if it is being "
|
|
"picked up as part of a regex" % (
|
|
newifaceobjlist[objidx].name,
|
|
str(newifaceobjlist[objidx].upperifaces),
|
|
newifaceobjlist[objidx].name
|
|
)
|
|
)
|
|
|
|
if print_warning:
|
|
self.logger.warning(warning_no_config_regex)
|
|
else:
|
|
# The warning shouldn't be printed because we've detected that this
|
|
# interface was pick up as part of a regex but the config doesn't
|
|
# exist anymore. It was most likely removed from the config file itself
|
|
# We should down this interface and remove it from the ifaceobjdict
|
|
# and dependency graph used for the following ifreload.
|
|
ifname_to_remove = newifaceobjlist[objidx].name
|
|
ifacedownlist.append(ifname_to_remove)
|
|
|
|
try:
|
|
if new_ifaceobjdict:
|
|
del new_ifaceobjdict[ifname_to_remove]
|
|
|
|
for k, v in new_dependency_graph.items():
|
|
if ifname_to_remove in v:
|
|
v.remove(ifname_to_remove)
|
|
del new_dependency_graph[ifname_to_remove]
|
|
except Exception as e:
|
|
self.logger.warning(warning_no_config_regex)
|
|
self.logger.warning("while trying to fix this situation "
|
|
"we ran into the following issues: %s" % str(e))
|
|
|
|
elif (lastifaceobjlist[0].link_kind and
|
|
not newifaceobjlist[0].link_kind):
|
|
self.logger.warning('%s: moved from being a %s to a'
|
|
' physical interface (non-logical interface).'
|
|
'This interface will be downed.\n'
|
|
' If this was not intentional, please restore the'
|
|
' original interface definition and execute ifreload'
|
|
% (newifaceobjlist[objidx].name,
|
|
ifaceLinkKind.to_str(lastifaceobjlist[0].link_kind)))
|
|
ifacedownlist.append(newifaceobjlist[objidx].name)
|
|
if not down_changed:
|
|
continue
|
|
if len(newifaceobjlist) != len(lastifaceobjlist):
|
|
ifacedownlist.append(ifname)
|
|
continue
|
|
|
|
# If interface has changed between the current file
|
|
# and the last installed append it to the down list
|
|
# compare object list
|
|
for objidx in range(0, len(lastifaceobjlist)):
|
|
oldobj = lastifaceobjlist[objidx]
|
|
newobj = newifaceobjlist[objidx]
|
|
if not newobj.compare(oldobj):
|
|
ifacedownlist.append(ifname)
|
|
continue
|
|
|
|
if ifacedownlist:
|
|
self.logger.info('reload: scheduling down on interfaces: %s'
|
|
%str(ifacedownlist))
|
|
# reinitialize dependency graph
|
|
self.dependency_graph = OrderedDict({})
|
|
|
|
# Generate dependency info for old config
|
|
self.flags.CHECK_SHARED_DEPENDENTS = False
|
|
self.populate_dependency_info(downops, ifacedownlist)
|
|
self.flags.CHECK_SHARED_DEPENDENTS = True
|
|
|
|
try:
|
|
# XXX: Hack to skip checking upperifaces during down.
|
|
# the dependency list is not complete here
|
|
# and we dont want to down the upperiface.
|
|
# Hence during reload, set this to true.
|
|
# This is being added to avoid a failure in
|
|
# scheduler._check_upperifaces when we are dowing
|
|
# a builtin bridge port
|
|
self.flags.SCHED_SKIP_CHECK_UPPERIFACES = True
|
|
self._sched_ifaces(ifacedownlist, downops,
|
|
followdependents=False,
|
|
sort=True)
|
|
except Exception as e:
|
|
self.logger.error(str(e))
|
|
pass
|
|
finally:
|
|
self.flags.SCHED_SKIP_CHECK_UPPERIFACES = False
|
|
self._process_delay_admin_state_queue('down')
|
|
else:
|
|
self.logger.info('no interfaces to down ..')
|
|
|
|
# Now, run 'up' with new config dict
|
|
# reset statemanager update flag to default
|
|
if not new_ifaceobjdict:
|
|
self.logger.debug('no interfaces to up')
|
|
return
|
|
|
|
if auto:
|
|
ifupdownflags.flags.ALL = True
|
|
ifupdownflags.flags.WITH_DEPENDS = True
|
|
# and now, we are back to the current config in ifaceobjdict
|
|
self.ifaceobjdict = new_ifaceobjdict
|
|
self.dependency_graph = new_dependency_graph
|
|
|
|
self.logger.info('reload: scheduling up on interfaces: %s'
|
|
%str(new_filtered_ifacenames))
|
|
ifupdownflags.flags.CACHE = True
|
|
try:
|
|
ret = self._sched_ifaces(new_filtered_ifacenames, upops,
|
|
followdependents=True
|
|
if ifupdownflags.flags.WITH_DEPENDS
|
|
else False)
|
|
except Exception as e:
|
|
ret = None
|
|
self.logger.error(str(e))
|
|
finally:
|
|
self._process_delay_admin_state_queue('up')
|
|
if ifupdownflags.flags.DRYRUN:
|
|
return
|
|
self._save_state()
|
|
|
|
if not iface_read_ret or not ret:
|
|
raise Exception()
|
|
|
|
def reload(self, *args, **kargs):
|
|
""" reload interface config """
|
|
self.logger.debug('reloading interface config ..')
|
|
if kargs.get('currentlyup', False):
|
|
self._reload_currentlyup(*args, **kargs)
|
|
else:
|
|
self._reload_default(*args, **kargs)
|
|
|
|
self.check_running_configuration([], all=True)
|
|
|
|
def _any_iface_errors(self, ifacenames):
|
|
for i in ifacenames:
|
|
ifaceobjs = self.get_ifaceobjs(i)
|
|
if not ifaceobjs: continue
|
|
for ifaceobj in ifaceobjs:
|
|
if (ifaceobj.status == ifaceStatus.NOTFOUND or
|
|
ifaceobj.status == ifaceStatus.ERROR):
|
|
return True
|
|
return False
|
|
|
|
def _pretty_print_ordered_dict(self, prefix, argdict):
|
|
outbuf = prefix + ' {\n'
|
|
for k, vlist in list(argdict.items()):
|
|
outbuf += '\t%s : %s\n' %(k, str(vlist))
|
|
self.logger.debug(outbuf + '}')
|
|
|
|
def print_dependency(self, ifacenames, format):
|
|
""" prints iface dependency information """
|
|
|
|
if not ifacenames:
|
|
ifacenames = list(self.ifaceobjdict.keys())
|
|
if format == 'list':
|
|
for k,v in list(self.dependency_graph.items()):
|
|
print('%s : %s' %(k, str(v)))
|
|
elif format == 'dot':
|
|
indegrees = {}
|
|
for i in list(self.dependency_graph.keys()):
|
|
indegrees.update({i: self.get_iface_refcnt(i)})
|
|
graph.generate_dots(self.dependency_graph, indegrees)
|
|
|
|
def print_ifaceobjs_list(self, ifacenames):
|
|
for i in ifacenames:
|
|
print(i)
|
|
|
|
def print_ifaceobjs_raw(self, ifacenames, format=None):
|
|
""" prints raw lines for ifaces from config file """
|
|
|
|
if format == "json":
|
|
self.print_ifaceobjs_pretty(ifacenames, format)
|
|
return
|
|
|
|
for i in ifacenames:
|
|
for ifaceobj in self.get_ifaceobjs(i):
|
|
if self.is_ifaceobj_builtin(ifaceobj):
|
|
continue
|
|
ifaceobj.dump_raw(self.logger)
|
|
if (ifupdownflags.flags.WITH_DEPENDS and
|
|
not ifupdownflags.flags.ALL):
|
|
dlist = ifaceobj.lowerifaces
|
|
if not dlist: continue
|
|
self.print_ifaceobjs_raw(dlist)
|
|
|
|
def _get_ifaceobjs_pretty(self, ifacenames, ifaceobjs, running=False):
|
|
""" returns iface obj list """
|
|
|
|
for i in ifacenames:
|
|
for ifaceobj in self.get_ifaceobjs(i):
|
|
if ((not running and self.is_ifaceobj_noconfig(ifaceobj)) or
|
|
(running and not ifaceobj.is_config_present() and
|
|
not self.is_iface_builtin_byname(i) and
|
|
not ifaceobj.upperifaces)):
|
|
continue
|
|
ifaceobjs.append(ifaceobj)
|
|
if (ifupdownflags.flags.WITH_DEPENDS and
|
|
not ifupdownflags.flags.ALL):
|
|
dlist = ifaceobj.lowerifaces
|
|
if not dlist: continue
|
|
self._get_ifaceobjs_pretty(dlist, ifaceobjs, running)
|
|
|
|
def print_ifaceobjs_pretty(self, ifacenames, format='native'):
|
|
""" pretty prints iface in format given by keyword arg format """
|
|
ifaceobjs = []
|
|
self._get_ifaceobjs_pretty(ifacenames, ifaceobjs)
|
|
if not ifaceobjs: return
|
|
if format == 'json':
|
|
print(json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
|
|
indent=4, separators=(',', ': ')))
|
|
else:
|
|
expand = int(self.config.get('ifquery_ifacename_expand_range', '0'))
|
|
for i in ifaceobjs:
|
|
if not expand and (i.flags & iface.IFACERANGE_ENTRY):
|
|
# print only the first one
|
|
if i.flags & iface.IFACERANGE_START:
|
|
i.dump_pretty(use_realname=True)
|
|
else:
|
|
i.dump_pretty()
|
|
|
|
def _get_ifaceobjscurr_pretty(self, ifacenames, ifaceobjs):
|
|
ret = 0
|
|
for i in ifacenames:
|
|
ifaceobjscurr = self.get_ifaceobjcurr(i)
|
|
if not ifaceobjscurr: continue
|
|
for ifaceobj in ifaceobjscurr:
|
|
if (ifaceobj.status == ifaceStatus.NOTFOUND or
|
|
ifaceobj.status == ifaceStatus.ERROR):
|
|
ret = 1
|
|
if self.is_ifaceobj_noconfig(ifaceobj):
|
|
continue
|
|
ifaceobjs.append(ifaceobj)
|
|
if (ifupdownflags.flags.WITH_DEPENDS and
|
|
not ifupdownflags.flags.ALL):
|
|
dlist = ifaceobj.lowerifaces
|
|
if not dlist: continue
|
|
dret = self._get_ifaceobjscurr_pretty(dlist, ifaceobjs)
|
|
if dret: ret = 1
|
|
return ret
|
|
|
|
def print_ifaceobjscurr_pretty(self, ifacenames, format='native'):
|
|
""" pretty prints current running state of interfaces with status.
|
|
|
|
returns 1 if any of the interface has an error,
|
|
else returns 0
|
|
"""
|
|
ifaceobjs = []
|
|
ret = self._get_ifaceobjscurr_pretty(ifacenames, ifaceobjs)
|
|
if not ifaceobjs: return
|
|
|
|
# override ifaceStatusUserStrs
|
|
ifaceStatusUserStrs.SUCCESS = self.config.get('ifquery_check_success_str', _success_sym)
|
|
ifaceStatusUserStrs.ERROR = self.config.get('ifquery_check_error_str', _error_sym)
|
|
ifaceStatusUserStrs.UNKNOWN = self.config.get('ifquery_check_unknown_str', '')
|
|
if format == 'json':
|
|
print(json.dumps(ifaceobjs, cls=ifaceJsonEncoderWithStatus,
|
|
indent=2, separators=(',', ': ')))
|
|
else:
|
|
for ifaceobj in ifaceobjs:
|
|
ifaceobj.dump_pretty(with_status=True)
|
|
return ret
|
|
|
|
def print_ifaceobjsrunning_pretty(self, ifacenames, format='native'):
|
|
""" pretty prints iface running state """
|
|
|
|
ifaceobjs = []
|
|
self._get_ifaceobjs_pretty(ifacenames, ifaceobjs, running=True)
|
|
if not ifaceobjs: return
|
|
if format == 'json':
|
|
print(json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=2,
|
|
separators=(',', ': ')))
|
|
else:
|
|
for i in ifaceobjs:
|
|
i.dump_pretty()
|
|
|
|
def _dump(self):
|
|
print('ifupdown main object dump')
|
|
print(self.pp.pprint(self.modules))
|
|
print(self.pp.pprint(self.ifaceobjdict))
|
|
|
|
def _dump_ifaceobjs(self, ifacenames):
|
|
for i in ifacenames:
|
|
ifaceobjs = self.get_ifaceobjs(i)
|
|
for i in ifaceobjs:
|
|
i.dump(self.logger)
|
|
print('\n')
|