mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
move ifupdown2/* .
ifupdown2 code was one level deeper because ifupdown2 initially had ifupdown2 and ifupdown2-addons as two separate packages. Since they were combined into one package, it makes sense to move all combined code under the top level directory Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
This commit is contained in:
5
ifupdown/__init__.py
Normal file
5
ifupdown/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
""" ifupdown2 package.
|
||||
|
||||
.. moduleauthor:: Roopa Prabhu <roopa@cumulusnetworks.com>
|
||||
|
||||
"""
|
23
ifupdown/exceptions.py
Normal file
23
ifupdown/exceptions.py
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
# ifupdown --
|
||||
# exceptions
|
||||
#
|
||||
|
||||
class Error(Exception):
|
||||
"""Base class for exceptions in ifupdown"""
|
||||
|
||||
pass
|
||||
|
||||
class ifaceNotFoundError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class invalidValueError(Error):
|
||||
pass
|
||||
|
||||
class errorReadingStateError(Error):
|
||||
pass
|
89
ifupdown/graph.py
Normal file
89
ifupdown/graph.py
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
# graph --
|
||||
# graph helper module for ifupdown
|
||||
#
|
||||
|
||||
import logging
|
||||
import copy
|
||||
from collections import deque
|
||||
try:
|
||||
from gvgen import *
|
||||
except ImportError as e:
|
||||
pass
|
||||
|
||||
class graph():
|
||||
""" graph functions to sort and print interface graph """
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger('ifupdown.' +
|
||||
self.__class__.__name__)
|
||||
|
||||
@classmethod
|
||||
def topological_sort_graphs_all(cls, dependency_graphs, indegrees_arg):
|
||||
""" runs topological sort on interface list passed as dependency graph
|
||||
|
||||
Args:
|
||||
**dependency_graphs** (dict): dependency graph with dependency
|
||||
lists for interfaces
|
||||
|
||||
**indegrees_arg** (dict): indegrees array for all interfaces
|
||||
"""
|
||||
S = []
|
||||
Q = deque()
|
||||
|
||||
indegrees = copy.deepcopy(indegrees_arg)
|
||||
for ifname,indegree in indegrees.items():
|
||||
if indegree == 0:
|
||||
Q.append(ifname)
|
||||
|
||||
while len(Q):
|
||||
# initialize queue
|
||||
x = Q.popleft()
|
||||
|
||||
# Get dependents of x
|
||||
dlist = dependency_graphs.get(x)
|
||||
if not dlist:
|
||||
S.append(x)
|
||||
continue
|
||||
|
||||
for y in dlist:
|
||||
indegrees[y] = indegrees.get(y) - 1
|
||||
if indegrees.get(y) == 0:
|
||||
Q.append(y)
|
||||
|
||||
S.append(x)
|
||||
|
||||
for ifname,indegree in indegrees.items():
|
||||
if indegree != 0:
|
||||
raise Exception('cycle found involving iface %s' %ifname +
|
||||
' (indegree %d)' %indegree)
|
||||
|
||||
return S
|
||||
|
||||
@classmethod
|
||||
def generate_dots(cls, dependency_graph, indegrees):
|
||||
""" spits out interface dependency graph in dot format
|
||||
|
||||
Args:
|
||||
**dependency_graphs** (dict): dependency graph with dependency
|
||||
lists for interfaces
|
||||
|
||||
**indegrees_arg** (dict): indegrees array for all interfaces
|
||||
"""
|
||||
|
||||
gvgraph = GvGen()
|
||||
graphnodes = {}
|
||||
for v in dependency_graph.keys():
|
||||
graphnodes[v] = gvgraph.newItem(v)
|
||||
|
||||
for i, v in graphnodes.items():
|
||||
dlist = dependency_graph.get(i, [])
|
||||
if not dlist:
|
||||
continue
|
||||
for d in dlist:
|
||||
gvgraph.newLink(v, graphnodes.get(d))
|
||||
gvgraph.dot()
|
593
ifupdown/iface.py
Normal file
593
ifupdown/iface.py
Normal file
@@ -0,0 +1,593 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
# iface --
|
||||
# interface object
|
||||
#
|
||||
|
||||
"""ifupdown2 network interface object
|
||||
|
||||
It is modeled based on the 'iface' section in /etc/network/interfaces
|
||||
file. But can be extended to include any other network interface format
|
||||
"""
|
||||
|
||||
from collections import OrderedDict
|
||||
import logging
|
||||
import json
|
||||
|
||||
class ifaceType():
|
||||
UNKNOWN = 0x0
|
||||
IFACE = 0x1
|
||||
BRIDGE_VLAN = 0x2
|
||||
|
||||
|
||||
class ifaceRole():
|
||||
""" ifaceRole is used to classify the ifaceobj.role of
|
||||
MASTER or SLAVE where there is a bond or bridge
|
||||
with bond-slaves or bridge-ports. A bond in a bridge
|
||||
is both a master and slave (0x3)
|
||||
"""
|
||||
UNKNOWN = 0x0
|
||||
SLAVE = 0x1
|
||||
MASTER = 0x2
|
||||
|
||||
class ifaceLinkKind():
|
||||
""" ifaceLlinkKind is used to identify interfaces
|
||||
in the ifaceobj.link_kind attribute. Dependents of the bridge or
|
||||
bond have an ifaceobj.role attribute of SLAVE and the bridge or
|
||||
bond itself has ifaceobj.role of MASTER.
|
||||
"""
|
||||
UNKNOWN = 0x0
|
||||
BRIDGE = 0x1
|
||||
BOND = 0x2
|
||||
VLAN = 0x4
|
||||
VXLAN = 0x8
|
||||
|
||||
class ifaceLinkType():
|
||||
LINK_UNKNOWN = 0x0
|
||||
LINK_SLAVE = 0x1
|
||||
LINK_MASTER = 0x2
|
||||
LINK_NA = 0x3
|
||||
|
||||
class ifaceDependencyType():
|
||||
""" Indicates type of dependency.
|
||||
|
||||
This class enumerates types of dependency relationships
|
||||
between interfaces.
|
||||
|
||||
iface dependency relationships can be classified
|
||||
into:
|
||||
- link
|
||||
- master/slave
|
||||
|
||||
In a 'link' dependency relationship, dependency can be shared
|
||||
between interfaces. example: swp1.100 and
|
||||
swp1.200 can both have 'link' swp1. swp1 is also a dependency
|
||||
of swp1.100 and swp1.200. As you can see dependency
|
||||
swp1 is shared between swp1.100 and swp1.200.
|
||||
|
||||
In a master/slave relationship like bridge and
|
||||
its ports: eg: bridge br0 and its ports swp1 and swp2.
|
||||
dependency swp1 and swp2 cannot be shared with any other
|
||||
interface with the same dependency relationship.
|
||||
ie, swp1 and swp2 cannot be in a slave relationship
|
||||
with another interface. Understanding the dependency type is
|
||||
required for any semantic checks between dependencies.
|
||||
|
||||
"""
|
||||
UNKNOWN = 0x0
|
||||
LINK = 0x1
|
||||
MASTER_SLAVE = 0x2
|
||||
|
||||
class ifaceStatus():
|
||||
"""Enumerates iface status """
|
||||
|
||||
UNKNOWN = 0x1
|
||||
SUCCESS = 0x2
|
||||
ERROR = 0x3
|
||||
NOTFOUND = 0x4
|
||||
|
||||
@classmethod
|
||||
def to_str(cls, state):
|
||||
if state == cls.UNKNOWN:
|
||||
return 'unknown'
|
||||
elif state == cls.SUCCESS:
|
||||
return 'success'
|
||||
elif state == cls.ERROR:
|
||||
return 'error'
|
||||
elif state == cls.NOTFOUND:
|
||||
return 'notfound'
|
||||
|
||||
@classmethod
|
||||
def from_str(cls, state_str):
|
||||
if state_str == 'unknown':
|
||||
return cls.UNKNOWN
|
||||
elif state_str == 'success':
|
||||
return cls.SUCCESS
|
||||
elif state_str == 'error':
|
||||
return cls.ERROR
|
||||
|
||||
class ifaceState():
|
||||
"""Enumerates iface state """
|
||||
|
||||
UNKNOWN = 0x1
|
||||
NEW = 0x2
|
||||
PRE_UP = 0x3
|
||||
UP = 0x4
|
||||
POST_UP = 0x5
|
||||
PRE_DOWN = 0x6
|
||||
DOWN = 0x7
|
||||
POST_DOWN = 0x8
|
||||
|
||||
# Pseudo states
|
||||
QUERY_CHECKCURR = 0x9
|
||||
QUERY_RUNNING = 0xa
|
||||
|
||||
@classmethod
|
||||
def to_str(cls, state):
|
||||
if state == cls.UNKNOWN:
|
||||
return 'unknown'
|
||||
elif state == cls.NEW:
|
||||
return 'new'
|
||||
elif state == cls.PRE_UP:
|
||||
return 'pre-up'
|
||||
elif state == cls.UP:
|
||||
return 'up'
|
||||
elif state == cls.POST_UP:
|
||||
return 'post-up'
|
||||
elif state == cls.PRE_DOWN:
|
||||
return 'pre-down'
|
||||
elif state == cls.DOWN:
|
||||
return 'down'
|
||||
elif state == cls.POST_DOWN:
|
||||
return 'post-down'
|
||||
elif state == cls.QUERY_CHECKCURR:
|
||||
return 'query-checkcurr'
|
||||
elif state == cls.QUERY_RUNNING:
|
||||
return 'query-running'
|
||||
|
||||
@classmethod
|
||||
def from_str(cls, state_str):
|
||||
if state_str == 'unknown':
|
||||
return cls.UNKNOWN
|
||||
elif state_str == 'new':
|
||||
return cls.NEW
|
||||
elif state_str == 'pre-up':
|
||||
return cls.PRE_UP
|
||||
elif state_str == 'up':
|
||||
return cls.UP
|
||||
elif state_str == 'post-up':
|
||||
return cls.POST_UP
|
||||
elif state_str == 'pre-down':
|
||||
return cls.PRE_DOWN
|
||||
elif state_str == 'down':
|
||||
return cls.DOWN
|
||||
elif state_str == 'post-down':
|
||||
return cls.POST_DOWN
|
||||
elif state_str == 'query-checkcurr':
|
||||
return cls.QUERY_CHECKCURR
|
||||
elif state_str == 'query-running':
|
||||
return cls.QUERY_RUNNING
|
||||
|
||||
class ifaceJsonEncoder(json.JSONEncoder):
|
||||
def default(self, o):
|
||||
retconfig = {}
|
||||
if o.config:
|
||||
retconfig = dict((k, (v[0] if len(v) == 1 else v))
|
||||
for k,v in o.config.items())
|
||||
return OrderedDict({'name' : o.name,
|
||||
'addr_method' : o.addr_method,
|
||||
'addr_family' : o.addr_family,
|
||||
'auto' : o.auto,
|
||||
'config' : retconfig})
|
||||
|
||||
class ifaceJsonDecoder():
|
||||
@classmethod
|
||||
def json_to_ifaceobj(cls, ifaceattrdict):
|
||||
ifaceattrdict['config'] = OrderedDict([(k, (v if isinstance(v, list)
|
||||
else [v]))
|
||||
for k,v in ifaceattrdict.get('config',
|
||||
OrderedDict()).items()])
|
||||
return iface(attrsdict=ifaceattrdict)
|
||||
|
||||
class iface():
|
||||
""" ifupdown2 iface object class
|
||||
|
||||
Attributes:
|
||||
**name** Name of the interface
|
||||
|
||||
**addr_family** Address family eg, inet, inet6. Can be None to
|
||||
indicate both address families
|
||||
|
||||
**addr_method** Address method eg, static, manual or None for
|
||||
static address method
|
||||
|
||||
**config** dictionary of config lines for this interface
|
||||
|
||||
**state** Configuration state of an interface as defined by
|
||||
ifaceState
|
||||
|
||||
**status** Configuration status of an interface as defined by
|
||||
ifaceStatus
|
||||
|
||||
**flags** Internal flags used by iface processing
|
||||
|
||||
**priv_flags** private flags owned by module using this class
|
||||
|
||||
**module_flags** module flags owned by module using this class
|
||||
|
||||
**refcnt** reference count, indicating number of interfaces
|
||||
dependent on this iface
|
||||
|
||||
**lowerifaces** list of interface names lower to this interface or
|
||||
this interface depends on
|
||||
|
||||
**upperifaces** list of interface names upper to this interface or
|
||||
the interfaces that depend on this interface
|
||||
|
||||
**auto** True if interface belongs to the auto class
|
||||
|
||||
**classes** List of classes the interface belongs to
|
||||
|
||||
**env** shell environment the interface needs during
|
||||
execution
|
||||
|
||||
**raw_config** raw interface config from file
|
||||
"""
|
||||
|
||||
# flag to indicate that the object was created from pickled state
|
||||
_PICKLED = 0x00000001
|
||||
HAS_SIBLINGS = 0x00000010
|
||||
IFACERANGE_ENTRY = 0x00000100
|
||||
IFACERANGE_START = 0x00001000
|
||||
|
||||
version = '0.1'
|
||||
|
||||
def __init__(self, attrsdict={}):
|
||||
self._set_attrs_from_dict(attrsdict)
|
||||
self._config_status = {}
|
||||
"""dict with config status of iface attributes"""
|
||||
self.state = ifaceState.NEW
|
||||
"""iface state (of type ifaceState) """
|
||||
self.status = ifaceStatus.UNKNOWN
|
||||
"""iface status (of type ifaceStatus) """
|
||||
self.status_str = None
|
||||
"""iface status str (string representing the status) """
|
||||
self.flags = 0x0
|
||||
"""iface flags """
|
||||
self.priv_flags = 0x0
|
||||
"""iface module flags dictionary with module name: flags"""
|
||||
self.module_flags = {}
|
||||
"""iface priv flags. can be used by the external object manager """
|
||||
self.refcnt = 0
|
||||
"""iface refcnt (incremented for each dependent this interface has) """
|
||||
self.lowerifaces = None
|
||||
"""lower iface list (in other words: slaves of this interface """
|
||||
self.upperifaces = None
|
||||
"""upper iface list (in other words: master of this interface """
|
||||
self.classes = []
|
||||
"""interface classes this iface belongs to """
|
||||
self.env = None
|
||||
"""environment variable dict required for this interface to run"""
|
||||
self.raw_config = []
|
||||
"""interface config/attributes in raw format (eg: as it appeared in the interfaces file)"""
|
||||
self.linkstate = None
|
||||
"""linkstate of the interface"""
|
||||
self.type = ifaceType.UNKNOWN
|
||||
"""interface type"""
|
||||
self.priv_data = None
|
||||
self.role = ifaceRole.UNKNOWN
|
||||
self.realname = None
|
||||
self.link_type = ifaceLinkType.LINK_UNKNOWN
|
||||
self.link_kind = ifaceLinkKind.UNKNOWN
|
||||
|
||||
# The below attribute is used to disambiguate between various
|
||||
# types of dependencies
|
||||
self.dependency_type = ifaceDependencyType.UNKNOWN
|
||||
|
||||
def _set_attrs_from_dict(self, attrdict):
|
||||
self.auto = attrdict.get('auto', False)
|
||||
self.name = attrdict.get('name')
|
||||
self.addr_family = attrdict.get('addr_family')
|
||||
self.addr_method = attrdict.get('addr_method')
|
||||
self.config = attrdict.get('config', OrderedDict())
|
||||
|
||||
def inc_refcnt(self):
|
||||
""" increment refcnt of the interface. Usually used to indicate that
|
||||
it has dependents """
|
||||
self.refcnt += 1
|
||||
|
||||
def dec_refcnt(self):
|
||||
""" decrement refcnt of the interface. Usually used to indicate that
|
||||
it has lost its dependent """
|
||||
self.refcnt -= 1
|
||||
|
||||
def is_config_present(self):
|
||||
""" returns true if the interface has user provided config,
|
||||
false otherwise """
|
||||
addr_method = self.addr_method
|
||||
if addr_method and addr_method in ['dhcp', 'dhcp6', 'loopback']:
|
||||
return True
|
||||
if not self.config:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def set_class(self, classname):
|
||||
""" appends class to the interfaces class list """
|
||||
self.classes.append(classname)
|
||||
|
||||
def set_state_n_status(self, state, status):
|
||||
""" sets state and status of an interface """
|
||||
self.state = state
|
||||
self.status = status
|
||||
|
||||
def set_flag(self, flag):
|
||||
self.flags |= flag
|
||||
|
||||
def clear_flag(self, flag):
|
||||
self.flags &= ~flag
|
||||
|
||||
def add_to_upperifaces(self, upperifacename):
|
||||
""" add to the list of upperifaces """
|
||||
if self.upperifaces:
|
||||
if upperifacename not in self.upperifaces:
|
||||
self.upperifaces.append(upperifacename)
|
||||
else:
|
||||
self.upperifaces = [upperifacename]
|
||||
|
||||
def get_attr_value(self, attr_name):
|
||||
""" add to the list of upperifaces """
|
||||
return self.config.get(attr_name)
|
||||
|
||||
def get_attr_value_first(self, attr_name):
|
||||
""" get first value of the specified attr name """
|
||||
attr_value_list = self.config.get(attr_name)
|
||||
if attr_value_list:
|
||||
return attr_value_list[0]
|
||||
return None
|
||||
|
||||
def get_attrs_value_first(self, attrs):
|
||||
""" get first value of the first attr in the list.
|
||||
Useful when you have multiple attrs representing the
|
||||
same thing.
|
||||
"""
|
||||
for attr in attrs:
|
||||
attr_value_list = self.config.get(attr)
|
||||
if attr_value_list:
|
||||
return attr_value_list[0]
|
||||
return None
|
||||
|
||||
def get_attr_value_n(self, attr_name, attr_index):
|
||||
""" get n'th value of the specified attr name """
|
||||
attr_value_list = self.config.get(attr_name)
|
||||
if attr_value_list:
|
||||
try:
|
||||
return attr_value_list[attr_index]
|
||||
except:
|
||||
return None
|
||||
return None
|
||||
|
||||
@property
|
||||
def get_env(self):
|
||||
""" get shell environment variables the interface must execute in """
|
||||
if not self.env:
|
||||
self.generate_env()
|
||||
return self.env
|
||||
|
||||
def generate_env(self):
|
||||
""" generate shell environment variables dict interface must execute
|
||||
in. This is used to support legacy ifupdown scripts
|
||||
"""
|
||||
env = {}
|
||||
config = self.config
|
||||
env['IFACE'] = self.name
|
||||
for attr, attr_value in config.items():
|
||||
attr_env_name = 'IF_%s' %attr.upper()
|
||||
env[attr_env_name] = attr_value[0]
|
||||
if env:
|
||||
self.env = env
|
||||
|
||||
def update_config(self, attr_name, attr_value):
|
||||
""" add attribute name and value to the interface config """
|
||||
self.config.setdefault(attr_name, []).append(attr_value)
|
||||
|
||||
def replace_config(self, attr_name, attr_value):
|
||||
""" add attribute name and value to the interface config """
|
||||
self.config[attr_name] = [attr_value]
|
||||
|
||||
def delete_config(self, attr_name):
|
||||
""" add attribute name and value to the interface config """
|
||||
try:
|
||||
del self.config[attr_name]
|
||||
except:
|
||||
pass
|
||||
|
||||
def update_config_dict(self, attrdict):
|
||||
self.config.update(attrdict)
|
||||
|
||||
def update_config_with_status(self, attr_name, attr_value, attr_status=0):
|
||||
""" add attribute name and value to the interface config and also
|
||||
update the config_status dict with status of this attribute config """
|
||||
if not attr_value:
|
||||
attr_value = ''
|
||||
self.config.setdefault(attr_name, []).append(attr_value)
|
||||
self._config_status.setdefault(attr_name, []).append(attr_status)
|
||||
# set global iface state
|
||||
if attr_status == 1:
|
||||
self.status = ifaceStatus.ERROR
|
||||
elif self.status != ifaceStatus.ERROR:
|
||||
# Not already error, mark success
|
||||
self.status = ifaceStatus.SUCCESS
|
||||
|
||||
def check_n_update_config_with_status_many(self, ifaceobjorig, attr_names,
|
||||
attr_status=0):
|
||||
# set multiple attribute status to zero
|
||||
# also updates status only if the attribute is present
|
||||
for attr_name in attr_names:
|
||||
if not ifaceobjorig.get_attr_value_first(attr_name):
|
||||
continue
|
||||
self.config.setdefault(attr_name, []).append('')
|
||||
self._config_status.setdefault(attr_name, []).append(attr_status)
|
||||
|
||||
def get_config_attr_status(self, attr_name, idx=0):
|
||||
""" get status of a attribute config on this interface.
|
||||
|
||||
Looks at the iface _config_status dict"""
|
||||
return self._config_status.get(attr_name, [])[idx]
|
||||
|
||||
def compare(self, dstiface):
|
||||
""" compares iface object with iface object passed as argument
|
||||
|
||||
Returns True if object self is same as dstiface and False otherwise """
|
||||
|
||||
if self.name != dstiface.name: return False
|
||||
if self.type != dstiface.type: return False
|
||||
if self.addr_family != dstiface.addr_family: return False
|
||||
if self.addr_method != dstiface.addr_method: return False
|
||||
if self.auto != dstiface.auto: return False
|
||||
if self.classes != dstiface.classes: return False
|
||||
if len(self.config) != len(dstiface.config):
|
||||
return False
|
||||
if any(True for k in self.config if k not in dstiface.config):
|
||||
return False
|
||||
if any(True for k,v in self.config.items()
|
||||
if v != dstiface.config.get(k)): return False
|
||||
return True
|
||||
|
||||
|
||||
def __getstate__(self):
|
||||
odict = self.__dict__.copy()
|
||||
del odict['state']
|
||||
del odict['status']
|
||||
del odict['lowerifaces']
|
||||
del odict['upperifaces']
|
||||
del odict['refcnt']
|
||||
del odict['_config_status']
|
||||
del odict['flags']
|
||||
del odict['priv_flags']
|
||||
del odict['module_flags']
|
||||
del odict['raw_config']
|
||||
del odict['linkstate']
|
||||
del odict['env']
|
||||
del odict['link_type']
|
||||
del odict['link_kind']
|
||||
del odict['role']
|
||||
del odict['dependency_type']
|
||||
return odict
|
||||
|
||||
def __setstate__(self, dict):
|
||||
self.__dict__.update(dict)
|
||||
self._config_status = {}
|
||||
self.state = ifaceState.NEW
|
||||
self.status = ifaceStatus.UNKNOWN
|
||||
self.refcnt = 0
|
||||
self.flags = 0
|
||||
self.lowerifaces = None
|
||||
self.upperifaces = None
|
||||
self.linkstate = None
|
||||
self.env = None
|
||||
self.role = ifaceRole.UNKNOWN
|
||||
self.priv_flags = 0
|
||||
self.module_flags = {}
|
||||
self.raw_config = []
|
||||
self.flags |= self._PICKLED
|
||||
self.link_type = ifaceLinkType.LINK_NA
|
||||
self.link_kind = ifaceLinkKind.UNKNOWN
|
||||
self.dependency_type = ifaceDependencyType.UNKNOWN
|
||||
|
||||
def dump_raw(self, logger):
|
||||
indent = ' '
|
||||
if self.auto:
|
||||
print ('auto %s' %self.name)
|
||||
print (self.raw_config[0])
|
||||
for i in range(1, len(self.raw_config)):
|
||||
print(indent + self.raw_config[i])
|
||||
|
||||
def dump(self, logger):
|
||||
indent = '\t'
|
||||
logger.info(self.name + ' : {')
|
||||
logger.info(indent + 'family: %s' %self.addr_family)
|
||||
logger.info(indent + 'method: %s' %self.addr_method)
|
||||
logger.info(indent + 'flags: %x' %self.flags)
|
||||
logger.info(indent + 'state: %s'
|
||||
%ifaceState.to_str(self.state))
|
||||
logger.info(indent + 'status: %s'
|
||||
%ifaceStatus.to_str(self.status))
|
||||
logger.info(indent + 'refcnt: %d' %self.refcnt)
|
||||
d = self.lowerifaces
|
||||
if d:
|
||||
logger.info(indent + 'lowerdevs: %s' %str(d))
|
||||
else:
|
||||
logger.info(indent + 'lowerdevs: None')
|
||||
|
||||
logger.info(indent + 'config: ')
|
||||
config = self.config
|
||||
if config:
|
||||
logger.info(indent + indent + str(config))
|
||||
logger.info('}')
|
||||
|
||||
def dump_pretty(self, with_status=False,
|
||||
successstr='success', errorstr='error',
|
||||
unknownstr='unknown', use_realname=False):
|
||||
indent = '\t'
|
||||
outbuf = ''
|
||||
if use_realname and self.realname:
|
||||
name = '%s' %self.realname
|
||||
else:
|
||||
name = '%s' %self.name
|
||||
if self.auto:
|
||||
outbuf += 'auto %s\n' %name
|
||||
ifaceline = ''
|
||||
if self.type == ifaceType.BRIDGE_VLAN:
|
||||
ifaceline += 'vlan %s' %name
|
||||
else:
|
||||
ifaceline += 'iface %s' %name
|
||||
if self.addr_family:
|
||||
ifaceline += ' %s' %self.addr_family
|
||||
if self.addr_method:
|
||||
ifaceline += ' %s' %self.addr_method
|
||||
if with_status:
|
||||
status_str = None
|
||||
if (self.status == ifaceStatus.ERROR or
|
||||
self.status == ifaceStatus.NOTFOUND):
|
||||
if self.status_str:
|
||||
ifaceline += ' (%s)' %self.status_str
|
||||
status_str = errorstr
|
||||
elif self.status == ifaceStatus.SUCCESS:
|
||||
status_str = successstr
|
||||
if status_str:
|
||||
outbuf += '{0:65} {1:>8}'.format(ifaceline, status_str) + '\n'
|
||||
else:
|
||||
outbuf += ifaceline + '\n'
|
||||
if self.status == ifaceStatus.NOTFOUND:
|
||||
outbuf = (outbuf.encode('utf8')
|
||||
if isinstance(outbuf, unicode) else outbuf)
|
||||
print (outbuf + '\n')
|
||||
return
|
||||
else:
|
||||
outbuf += ifaceline + '\n'
|
||||
config = self.config
|
||||
if config:
|
||||
for cname, cvaluelist in config.items():
|
||||
idx = 0
|
||||
for cv in cvaluelist:
|
||||
if with_status:
|
||||
s = self.get_config_attr_status(cname, idx)
|
||||
if s == -1:
|
||||
status_str = unknownstr
|
||||
elif s == 1:
|
||||
status_str = errorstr
|
||||
elif s == 0:
|
||||
status_str = successstr
|
||||
outbuf += (indent + '{0:55} {1:>10}'.format(
|
||||
'%s %s' %(cname, cv), status_str)) + '\n'
|
||||
else:
|
||||
outbuf += indent + '%s %s\n' %(cname, cv)
|
||||
idx += 1
|
||||
if with_status:
|
||||
outbuf = (outbuf.encode('utf8')
|
||||
if isinstance(outbuf, unicode) else outbuf)
|
||||
print (outbuf)
|
36
ifupdown/iff.py
Normal file
36
ifupdown/iff.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
#
|
||||
# Author: Scott Feldman, sfeldma@cumulusnetworks.com
|
||||
#
|
||||
#
|
||||
# from /usr/include/linux/if.h
|
||||
#
|
||||
|
||||
# Standard interface flags (netdevice->flags).
|
||||
|
||||
IFF_UP = 0x1 # interface is up
|
||||
IFF_BROADCAST = 0x2 # broadcast address valid
|
||||
IFF_DEBUG = 0x4 # turn on debugging
|
||||
IFF_LOOPBACK = 0x8 # is a loopback net
|
||||
IFF_POINTOPOINT = 0x10 # interface is has p-p link
|
||||
IFF_NOTRAILERS = 0x20 # avoid use of trailers
|
||||
IFF_RUNNING = 0x40 # interface RFC2863 OPER_UP
|
||||
IFF_NOARP = 0x80 # no ARP protocol
|
||||
IFF_PROMISC = 0x100 # receive all packets
|
||||
IFF_ALLMULTI = 0x200 # receive all multicast packets
|
||||
|
||||
IFF_MASTER = 0x400 # master of a load balancer
|
||||
IFF_SLAVE = 0x800 # slave of a load balancer
|
||||
|
||||
IFF_MULTICAST = 0x1000 # Supports multicast
|
||||
|
||||
IFF_PORTSEL = 0x2000 # can set media type
|
||||
IFF_AUTOMEDIA = 0x4000 # auto media select active
|
||||
IFF_DYNAMIC = 0x8000 # dialup device with changing addresses
|
||||
|
||||
IFF_LOWER_UP = 0x10000 # driver signals L1 up
|
||||
IFF_DORMANT = 0x20000 # driver signals dormant
|
||||
|
||||
IFF_ECHO = 0x40000 # echo sent packets
|
72
ifupdown/ifupdownbase.py
Normal file
72
ifupdown/ifupdownbase.py
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
# ifupdownBase --
|
||||
# base object for various ifupdown objects
|
||||
#
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import re
|
||||
import os
|
||||
from iface import *
|
||||
import rtnetlink_api as rtnetlink_api
|
||||
|
||||
class ifupdownBase(object):
|
||||
|
||||
def __init__(self):
|
||||
modulename = self.__class__.__name__
|
||||
self.logger = logging.getLogger('ifupdown.' + modulename)
|
||||
|
||||
def exec_command(self, cmd, cmdenv=None, nowait=False):
|
||||
cmd_returncode = 0
|
||||
cmdout = ''
|
||||
try:
|
||||
self.logger.info('Executing ' + cmd)
|
||||
if self.DRYRUN:
|
||||
return cmdout
|
||||
ch = subprocess.Popen(cmd.split(),
|
||||
stdout=subprocess.PIPE,
|
||||
shell=False, env=cmdenv,
|
||||
stderr=subprocess.STDOUT,
|
||||
close_fds=True)
|
||||
cmdout = ch.communicate()[0]
|
||||
cmd_returncode = ch.wait()
|
||||
except OSError as e:
|
||||
raise Exception('could not execute ' + cmd +
|
||||
'(' + str(e) + ')')
|
||||
if cmd_returncode != 0:
|
||||
raise Exception('error executing cmd \'%s\'' %cmd +
|
||||
'\n(' + cmdout.strip('\n ') + ')')
|
||||
return cmdout
|
||||
|
||||
def ignore_error(self, errmsg):
|
||||
if (self.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()
|
||||
self.logger.warn(str)
|
||||
pass
|
||||
|
||||
def log_error(self, str):
|
||||
if self.ignore_error(str) == False:
|
||||
raise
|
||||
#raise Exception(str)
|
||||
else:
|
||||
pass
|
||||
|
||||
def link_exists(self, ifacename):
|
||||
return os.path.exists('/sys/class/net/%s' %ifacename)
|
||||
|
||||
def link_up(self, ifacename):
|
||||
rtnetlink_api.rtnl_api.link_set(ifacename, "up")
|
||||
|
||||
def link_down(self, ifacename):
|
||||
rtnetlink_api.rtnl_api.link_set(ifacename, "down")
|
1403
ifupdown/ifupdownmain.py
Normal file
1403
ifupdown/ifupdownmain.py
Normal file
File diff suppressed because it is too large
Load Diff
237
ifupdown/netlink.py
Normal file
237
ifupdown/netlink.py
Normal file
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Scott Feldman, sfeldma@cumulusnetworks.com
|
||||
#
|
||||
|
||||
from os import strerror
|
||||
import select
|
||||
from time import time
|
||||
import socket
|
||||
from ctypes import *
|
||||
from errno import *
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
#
|
||||
# from /usr/include/linux/netlink.h
|
||||
#
|
||||
|
||||
NETLINK_ROUTE = 0 # Routing/device hook
|
||||
NETLINK_UNUSED = 1 # Unused number
|
||||
NETLINK_USERSOCK = 2 # Reserved for user mode socket protocols
|
||||
NETLINK_FIREWALL = 3 # Firewalling hook
|
||||
NETLINK_INET_DIAG = 4 # INET socket monitoring
|
||||
NETLINK_NFLOG = 5 # netfilter/iptables ULOG
|
||||
NETLINK_XFRM = 6 # ipsec
|
||||
NETLINK_SELINUX = 7 # SELinux event notifications
|
||||
NETLINK_ISCSI = 8 # Open-iSCSI
|
||||
NETLINK_AUDIT = 9 # auditing
|
||||
NETLINK_FIB_LOOKUP = 10
|
||||
NETLINK_CONNECTOR = 11
|
||||
NETLINK_NETFILTER = 12 # netfilter subsystem
|
||||
NETLINK_IP6_FW = 13
|
||||
NETLINK_DNRTMSG = 14 # DECnet routing messages
|
||||
NETLINK_KOBJECT_UEVENT = 15 # Kernel messages to userspace
|
||||
NETLINK_GENERIC = 16
|
||||
NETLINK_SCSITRANSPORT = 18 # SCSI Transports
|
||||
NETLINK_ECRYPTFS = 19
|
||||
NETLINK_RDMA = 20
|
||||
NETLINK_CRYPTO = 21 # Crypto layer
|
||||
|
||||
NLMSG_NOOP = 1 # Nothing.
|
||||
NLMSG_ERROR = 2 # Error
|
||||
NLMSG_DONE = 3 # End of a dump
|
||||
NLMSG_OVERRUN = 4 # Data lost
|
||||
|
||||
NETLINK_NO_ENOBUFS = 5
|
||||
|
||||
SOL_NETLINK = 270
|
||||
|
||||
class Nlmsghdr(Structure):
|
||||
|
||||
_fields_ = [
|
||||
('nlmsg_len', c_uint32),
|
||||
('nlmsg_type', c_uint16),
|
||||
('nlmsg_flags', c_uint16),
|
||||
('nlmsg_seq', c_uint32),
|
||||
('nlmsg_pid', c_uint32)
|
||||
]
|
||||
|
||||
def dump(self):
|
||||
print ('nlmsg_len', self.nlmsg_len)
|
||||
print ('nlmsg_type', self.nlmsg_type)
|
||||
print ('nlmsg_flags 0x%04x' % self.nlmsg_flags)
|
||||
print ('nlmsg_seq', self.nlmsg_seq)
|
||||
print ('nlmsg_pid', self.nlmsg_pid)
|
||||
|
||||
# Flags values
|
||||
|
||||
NLM_F_REQUEST = 1 # It is request message.
|
||||
NLM_F_MULTI = 2 # Multipart message, terminated by NLMSG_DONE
|
||||
NLM_F_ACK = 4 # Reply with ack, with zero or error code
|
||||
NLM_F_ECHO = 8 # Echo this request
|
||||
NLM_F_DUMP_INTR = 16 # Dump was inconsistent due to sequence change
|
||||
|
||||
# Modifiers to GET request
|
||||
NLM_F_ROOT = 0x100 # specify tree root
|
||||
NLM_F_MATCH = 0x200 # return all matching
|
||||
NLM_F_ATOMIC = 0x400 # atomic GET
|
||||
NLM_F_DUMP = (NLM_F_ROOT|NLM_F_MATCH)
|
||||
|
||||
# Modifiers to NEW request
|
||||
NLM_F_REPLACE = 0x100 # Override existing
|
||||
NLM_F_EXCL = 0x200 # Do not touch, if it exists
|
||||
NLM_F_CREATE = 0x400 # Create, if it does not exist
|
||||
NLM_F_APPEND = 0x800 # Add to end of list
|
||||
|
||||
NLMSG_ALIGNTO = 4
|
||||
def NLMSG_ALIGN(len):
|
||||
return (len + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)
|
||||
def NLMSG_HDRLEN():
|
||||
return NLMSG_ALIGN(sizeof(Nlmsghdr))
|
||||
def NLMSG_LENGTH(len):
|
||||
return len + NLMSG_ALIGN(NLMSG_HDRLEN())
|
||||
def NLMSG_SPACE(len):
|
||||
return NLMSG_ALIGN(NLMSG_LENGTH(len))
|
||||
def NLMSG_DATA(nlh):
|
||||
return addressof(nlh) + NLMSG_LENGTH(0)
|
||||
def NLMSG_NEXT(nlh, len):
|
||||
cur = NLMSG_ALIGN(nlh.nlmsg_len)
|
||||
nlh = Nlmsghdr.from_address(addressof(nlh) + cur)
|
||||
return len - cur, nlh
|
||||
def NLMSG_OK(nlh, len):
|
||||
return len >= sizeof(Nlmsghdr) and \
|
||||
nlh.nlmsg_len >= sizeof(Nlmsghdr) and \
|
||||
nlh.nlmsg_len <= len
|
||||
|
||||
class Nlmsgerr(Structure):
|
||||
|
||||
_fields_ = [
|
||||
('error', c_int),
|
||||
('msg', Nlmsghdr),
|
||||
]
|
||||
|
||||
class NetlinkError(Exception):
|
||||
|
||||
def __init__(self, message):
|
||||
Exception.__init__(self, message)
|
||||
#print(message)
|
||||
|
||||
class Netlink(socket.socket):
|
||||
|
||||
def __init__(self, pid, proto):
|
||||
|
||||
self.pid = pid
|
||||
self.recvbuf = bytearray(8 * 1024)
|
||||
self.sendbuf = bytearray(8 * 1024)
|
||||
self.seq = int(time())
|
||||
|
||||
try:
|
||||
|
||||
socket.socket.__init__(self, socket.AF_NETLINK, \
|
||||
socket.SOCK_RAW, proto)
|
||||
self.setblocking(0)
|
||||
|
||||
# Need to turn off ENOBUFS for netlink socket otherwise
|
||||
# in a kernel overrun situation, the socket will return
|
||||
# ENOBUFS on socket recv and be stuck for future recvs.
|
||||
|
||||
self.setsockopt(SOL_NETLINK, NETLINK_NO_ENOBUFS, 1)
|
||||
|
||||
except OSError as e:
|
||||
raise NetlinkError("open: socket err: %s" %e.strerror)
|
||||
|
||||
def bind(self, groups, cb):
|
||||
|
||||
self._nl_cb = cb
|
||||
|
||||
try:
|
||||
socket.socket.bind(self, (self.pid, groups))
|
||||
|
||||
except OSError as e:
|
||||
raise NetlinkError("bind: socket err: %s" %e.strerror)
|
||||
|
||||
def sendall(self, string):
|
||||
try:
|
||||
socket.socket.sendall(self, string)
|
||||
except OSError as e:
|
||||
raise NetlinkError("send: socket err: %s" %e.strerror)
|
||||
|
||||
def _process_nlh(self, recv, nlh):
|
||||
while NLMSG_OK(nlh, recv):
|
||||
yield recv, nlh
|
||||
recv, nlh = NLMSG_NEXT(nlh, recv)
|
||||
|
||||
def process(self, tokens=[]):
|
||||
|
||||
found_done = False
|
||||
|
||||
try:
|
||||
recv, src_addr = self.recvfrom_into(self.recvbuf)
|
||||
if not recv:
|
||||
# EOF
|
||||
print ("EOF")
|
||||
return False
|
||||
|
||||
except OSError as e:
|
||||
if e.errno in [EINTR, EAGAIN]:
|
||||
return False
|
||||
raise NetlinkError("netlink: socket err: %s" %e.strerror)
|
||||
|
||||
nlh = Nlmsghdr.from_buffer(self.recvbuf)
|
||||
for recv, nlh in self._process_nlh(recv, nlh):
|
||||
|
||||
# print "type %u, seq %u, pid %u" % \
|
||||
# (nlh.nlmsg_type, nlh.nlmsg_seq, nlh.nlmsg_pid)
|
||||
|
||||
l = nlh.nlmsg_len - sizeof(Nlmsghdr)
|
||||
|
||||
if l < 0 or nlh.nlmsg_len > recv:
|
||||
raise NetlinkError("netlink: malformed msg: len %d" % \
|
||||
nlh.nlmsg_len)
|
||||
|
||||
if tokens:
|
||||
current = (nlh.nlmsg_pid, nlh.nlmsg_seq)
|
||||
if current not in tokens:
|
||||
continue
|
||||
|
||||
if nlh.nlmsg_type == NLMSG_DONE:
|
||||
found_done = True
|
||||
break
|
||||
|
||||
if nlh.nlmsg_type == NLMSG_ERROR:
|
||||
err = Nlmsgerr.from_address(NLMSG_DATA(nlh))
|
||||
if err.error == 0:
|
||||
return False
|
||||
raise NetlinkError("netlink: %s" % strerror(abs(err.error)))
|
||||
|
||||
if self._nl_cb:
|
||||
self._nl_cb(nlh)
|
||||
|
||||
if found_done:
|
||||
return False
|
||||
|
||||
remnant = recv - NLMSG_ALIGN(nlh.nlmsg_len) > 0
|
||||
if remnant:
|
||||
raise NetlinkError("netlink: remnant of size %d" % \
|
||||
remnant)
|
||||
|
||||
return True
|
||||
|
||||
def process_wait(self, tokens):
|
||||
while self.process(tokens):
|
||||
pass
|
||||
|
||||
def process_forever(self):
|
||||
epoll = select.epoll()
|
||||
epoll.register(self.fileno(), select.EPOLLIN)
|
||||
while True:
|
||||
events = epoll.poll()
|
||||
for fileno, event in events:
|
||||
if fileno == self.fileno():
|
||||
self.process()
|
||||
|
||||
def process_event(self, event):
|
||||
return self.process()
|
437
ifupdown/networkinterfaces.py
Normal file
437
ifupdown/networkinterfaces.py
Normal file
@@ -0,0 +1,437 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
# networkInterfaces --
|
||||
# ifupdown network interfaces file parser
|
||||
#
|
||||
|
||||
import collections
|
||||
import logging
|
||||
import glob
|
||||
import re
|
||||
import os
|
||||
import copy
|
||||
from utils import utils
|
||||
from iface import *
|
||||
from template import templateEngine
|
||||
|
||||
whitespaces = '\n\t\r '
|
||||
|
||||
class networkInterfaces():
|
||||
""" debian ifupdown /etc/network/interfaces file parser """
|
||||
|
||||
hotplugs = {}
|
||||
auto_ifaces = []
|
||||
callbacks = {}
|
||||
auto_all = False
|
||||
|
||||
_addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6'],
|
||||
'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6']}
|
||||
|
||||
def __init__(self, interfacesfile='/etc/network/interfaces',
|
||||
interfacesfileiobuf=None, interfacesfileformat='native',
|
||||
template_engine=None, template_lookuppath=None):
|
||||
"""This member function initializes the networkinterfaces parser object.
|
||||
|
||||
Kwargs:
|
||||
**interfacesfile** (str): path to the interfaces file (default is /etc/network/interfaces)
|
||||
|
||||
**interfacesfileiobuf** (object): interfaces file io stream
|
||||
|
||||
**interfacesfileformat** (str): format of interfaces file (choices are 'native' and 'json'. 'native' being the default)
|
||||
|
||||
**template_engine** (str): template engine name
|
||||
|
||||
**template_lookuppath** (str): template lookup path
|
||||
|
||||
Raises:
|
||||
AttributeError, KeyError """
|
||||
|
||||
self.logger = logging.getLogger('ifupdown.' +
|
||||
self.__class__.__name__)
|
||||
self.callbacks = {'iface_found' : None,
|
||||
'validateifaceattr' : None,
|
||||
'validateifaceobj' : None}
|
||||
self.allow_classes = {}
|
||||
self.interfacesfile = interfacesfile
|
||||
self.interfacesfileiobuf = interfacesfileiobuf
|
||||
self.interfacesfileformat = interfacesfileformat
|
||||
self._filestack = [self.interfacesfile]
|
||||
self._template_engine = templateEngine(template_engine,
|
||||
template_lookuppath)
|
||||
self._currentfile_has_template = False
|
||||
self._ws_split_regex = re.compile(r'[\s\t]\s*')
|
||||
|
||||
@property
|
||||
def _currentfile(self):
|
||||
try:
|
||||
return self._filestack[-1]
|
||||
except:
|
||||
return self.interfacesfile
|
||||
|
||||
def _parse_error(self, filename, lineno, msg):
|
||||
if lineno == -1 or self._currentfile_has_template:
|
||||
self.logger.error('%s: %s' %(filename, msg))
|
||||
else:
|
||||
self.logger.error('%s: line%d: %s' %(filename, lineno, msg))
|
||||
|
||||
def _parse_warn(self, filename, lineno, msg):
|
||||
if lineno == -1 or self._currentfile_has_template:
|
||||
self.logger.warn('%s: %s' %(filename, msg))
|
||||
else:
|
||||
self.logger.warn('%s: line%d: %s' %(filename, lineno, msg))
|
||||
|
||||
def _validate_addr_family(self, ifaceobj, lineno=-1):
|
||||
if ifaceobj.addr_family:
|
||||
if not self._addrfams.get(ifaceobj.addr_family):
|
||||
self._parse_error(self._currentfile, lineno,
|
||||
'iface %s: unsupported address family \'%s\''
|
||||
%(ifaceobj.name, ifaceobj.addr_family))
|
||||
ifaceobj.addr_family = None
|
||||
ifaceobj.addr_method = None
|
||||
return
|
||||
if ifaceobj.addr_method:
|
||||
if (ifaceobj.addr_method not in
|
||||
self._addrfams.get(ifaceobj.addr_family)):
|
||||
self._parse_error(self._currentfile, lineno,
|
||||
'iface %s: unsupported address method \'%s\''
|
||||
%(ifaceobj.name, ifaceobj.addr_method))
|
||||
else:
|
||||
ifaceobj.addr_method = 'static'
|
||||
|
||||
def subscribe(self, callback_name, callback_func):
|
||||
"""This member function registers callback functions.
|
||||
|
||||
Args:
|
||||
**callback_name** (str): callback function name (supported names: 'iface_found', 'validateifaceattr', 'validateifaceobj')
|
||||
|
||||
**callback_func** (function pointer): callback function pointer
|
||||
|
||||
Warns on error
|
||||
"""
|
||||
|
||||
if callback_name not in self.callbacks.keys():
|
||||
print ('warning: invalid callback ' + callback_name)
|
||||
return -1
|
||||
|
||||
self.callbacks[callback_name] = callback_func
|
||||
|
||||
def ignore_line(self, line):
|
||||
l = line.strip(whitespaces)
|
||||
if not l or l[0] == '#':
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def process_allow(self, lines, cur_idx, lineno):
|
||||
allow_line = lines[cur_idx]
|
||||
|
||||
words = re.split(self._ws_split_regex, allow_line)
|
||||
if len(words) <= 1:
|
||||
raise Exception('invalid allow line \'%s\' at line %d'
|
||||
%(allow_line, lineno))
|
||||
|
||||
allow_class = words[0].split('-')[1]
|
||||
ifacenames = words[1:]
|
||||
|
||||
if self.allow_classes.get(allow_class):
|
||||
for i in ifacenames:
|
||||
self.allow_classes[allow_class].append(i)
|
||||
else:
|
||||
self.allow_classes[allow_class] = ifacenames
|
||||
return 0
|
||||
|
||||
def process_source(self, lines, cur_idx, lineno):
|
||||
# Support regex
|
||||
self.logger.debug('processing sourced line ..\'%s\'' %lines[cur_idx])
|
||||
sourced_file = re.split(self._ws_split_regex, lines[cur_idx], 2)[1]
|
||||
if sourced_file:
|
||||
filenames = glob.glob(sourced_file)
|
||||
if not filenames:
|
||||
self._parse_warn(self._currentfile, lineno,
|
||||
'cannot find source file %s' %sourced_file)
|
||||
return 0
|
||||
for f in filenames:
|
||||
self.read_file(f)
|
||||
else:
|
||||
self._parse_error(self._currentfile, lineno,
|
||||
'unable to read source line')
|
||||
return 0
|
||||
|
||||
def process_auto(self, lines, cur_idx, lineno):
|
||||
auto_ifaces = re.split(self._ws_split_regex, lines[cur_idx])[1:]
|
||||
if not auto_ifaces:
|
||||
self._parse_error(self._currentfile, lineno,
|
||||
'invalid auto line \'%s\''%lines[cur_idx])
|
||||
return 0
|
||||
for a in auto_ifaces:
|
||||
if a == 'all':
|
||||
self.auto_all = True
|
||||
break
|
||||
r = utils.parse_iface_range(a)
|
||||
if r:
|
||||
for i in range(r[1], r[2]):
|
||||
self.auto_ifaces.append('%s-%d' %(r[0], i))
|
||||
self.auto_ifaces.append(a)
|
||||
return 0
|
||||
|
||||
def _add_to_iface_config(self, ifacename, iface_config, attrname,
|
||||
attrval, lineno):
|
||||
newattrname = attrname.replace("_", "-")
|
||||
try:
|
||||
if not self.callbacks.get('validateifaceattr')(newattrname,
|
||||
attrval):
|
||||
self._parse_error(self._currentfile, lineno,
|
||||
'iface %s: unsupported keyword (%s)'
|
||||
%(ifacename, attrname))
|
||||
return
|
||||
except:
|
||||
pass
|
||||
attrvallist = iface_config.get(newattrname, [])
|
||||
if newattrname in ['scope', 'netmask', 'broadcast', 'preferred-lifetime']:
|
||||
# For attributes that are related and that can have multiple
|
||||
# entries, store them at the same index as their parent attribute.
|
||||
# The example of such attributes is 'address' and its related
|
||||
# attributes. since the related attributes can be optional,
|
||||
# we add null string '' in places where they are optional.
|
||||
# XXX: this introduces awareness of attribute names in
|
||||
# this class which is a violation.
|
||||
|
||||
# get the index corresponding to the 'address'
|
||||
addrlist = iface_config.get('address')
|
||||
if addrlist:
|
||||
# find the index of last address element
|
||||
for i in range(0, len(addrlist) - len(attrvallist) -1):
|
||||
attrvallist.append('')
|
||||
attrvallist.append(attrval)
|
||||
iface_config[newattrname] = attrvallist
|
||||
elif not attrvallist:
|
||||
iface_config[newattrname] = [attrval]
|
||||
else:
|
||||
iface_config[newattrname].append(attrval)
|
||||
|
||||
def parse_iface(self, lines, cur_idx, lineno, ifaceobj):
|
||||
lines_consumed = 0
|
||||
line_idx = cur_idx
|
||||
|
||||
iface_line = lines[cur_idx].strip(whitespaces)
|
||||
iface_attrs = re.split(self._ws_split_regex, iface_line)
|
||||
ifacename = iface_attrs[1]
|
||||
|
||||
# in cases where mako is unable to render the template
|
||||
# or incorrectly renders it due to user template
|
||||
# errors, we maybe left with interface names with
|
||||
# mako variables in them. There is no easy way to
|
||||
# recognize and warn about these. In the below check
|
||||
# we try to warn the user of such cases by looking for
|
||||
# variable patterns ('$') in interface names.
|
||||
if '$' in ifacename:
|
||||
self._parse_warn(self._currentfile, lineno,
|
||||
'%s: unexpected characters in interface name' %ifacename)
|
||||
|
||||
ifaceobj.raw_config.append(iface_line)
|
||||
iface_config = collections.OrderedDict()
|
||||
for line_idx in range(cur_idx + 1, len(lines)):
|
||||
l = lines[line_idx].strip(whitespaces)
|
||||
if self.ignore_line(l) == 1:
|
||||
continue
|
||||
attrs = re.split(self._ws_split_regex, l, 1)
|
||||
if self._is_keyword(attrs[0]):
|
||||
line_idx -= 1
|
||||
break
|
||||
# if not a keyword, every line must have at least a key and value
|
||||
if len(attrs) < 2:
|
||||
self._parse_error(self._currentfile, line_idx,
|
||||
'iface %s: invalid syntax \'%s\'' %(ifacename, l))
|
||||
continue
|
||||
ifaceobj.raw_config.append(l)
|
||||
attrname = attrs[0]
|
||||
# preprocess vars (XXX: only preprocesses $IFACE for now)
|
||||
attrval = re.sub(r'\$IFACE', ifacename, attrs[1])
|
||||
self._add_to_iface_config(ifacename, iface_config, attrname,
|
||||
attrval, line_idx+1)
|
||||
lines_consumed = line_idx - cur_idx
|
||||
|
||||
# Create iface object
|
||||
if ifacename.find(':') != -1:
|
||||
ifaceobj.name = ifacename.split(':')[0]
|
||||
else:
|
||||
ifaceobj.name = ifacename
|
||||
|
||||
ifaceobj.config = iface_config
|
||||
ifaceobj.generate_env()
|
||||
|
||||
try:
|
||||
ifaceobj.addr_family = iface_attrs[2]
|
||||
ifaceobj.addr_method = iface_attrs[3]
|
||||
except IndexError:
|
||||
# ignore
|
||||
pass
|
||||
self._validate_addr_family(ifaceobj, lineno)
|
||||
|
||||
if self.auto_all or (ifaceobj.name in self.auto_ifaces):
|
||||
ifaceobj.auto = True
|
||||
|
||||
classes = self.get_allow_classes_for_iface(ifaceobj.name)
|
||||
if classes:
|
||||
[ifaceobj.set_class(c) for c in classes]
|
||||
|
||||
return lines_consumed # Return next index
|
||||
|
||||
def process_iface(self, lines, cur_idx, lineno):
|
||||
ifaceobj = iface()
|
||||
lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
|
||||
|
||||
range_val = utils.parse_iface_range(ifaceobj.name)
|
||||
if range_val:
|
||||
for v in range(range_val[1], range_val[2]):
|
||||
ifaceobj_new = copy.deepcopy(ifaceobj)
|
||||
ifaceobj_new.realname = '%s' %ifaceobj.name
|
||||
ifaceobj_new.name = '%s%d' %(range_val[0], v)
|
||||
ifaceobj_new.flags = iface.IFACERANGE_ENTRY
|
||||
if v == range_val[1]:
|
||||
ifaceobj_new.flags |= iface.IFACERANGE_START
|
||||
self.callbacks.get('iface_found')(ifaceobj_new)
|
||||
else:
|
||||
self.callbacks.get('iface_found')(ifaceobj)
|
||||
|
||||
return lines_consumed # Return next index
|
||||
|
||||
def process_vlan(self, lines, cur_idx, lineno):
|
||||
ifaceobj = iface()
|
||||
lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
|
||||
|
||||
range_val = utils.parse_iface_range(ifaceobj.name)
|
||||
if range_val:
|
||||
for v in range(range_val[1], range_val[2]):
|
||||
ifaceobj_new = copy.deepcopy(ifaceobj)
|
||||
ifaceobj_new.realname = '%s' %ifaceobj.name
|
||||
ifaceobj_new.name = '%s%d' %(range_val[0], v)
|
||||
ifaceobj_new.type = ifaceType.BRIDGE_VLAN
|
||||
ifaceobj_new.flags = iface.IFACERANGE_ENTRY
|
||||
if v == range_val[1]:
|
||||
ifaceobj_new.flags |= iface.IFACERANGE_START
|
||||
self.callbacks.get('iface_found')(ifaceobj_new)
|
||||
else:
|
||||
ifaceobj.type = ifaceType.BRIDGE_VLAN
|
||||
self.callbacks.get('iface_found')(ifaceobj)
|
||||
|
||||
return lines_consumed # Return next index
|
||||
|
||||
network_elems = { 'source' : process_source,
|
||||
'allow' : process_allow,
|
||||
'auto' : process_auto,
|
||||
'iface' : process_iface,
|
||||
'vlan' : process_vlan}
|
||||
|
||||
def _is_keyword(self, str):
|
||||
# The additional split here is for allow- keyword
|
||||
tmp_str = str.split('-')[0]
|
||||
if tmp_str in self.network_elems.keys():
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def _get_keyword_func(self, str):
|
||||
tmp_str = str.split('-')[0]
|
||||
return self.network_elems.get(tmp_str)
|
||||
|
||||
def get_allow_classes_for_iface(self, ifacename):
|
||||
classes = []
|
||||
for class_name, ifacenames in self.allow_classes.items():
|
||||
if ifacename in ifacenames:
|
||||
classes.append(class_name)
|
||||
return classes
|
||||
|
||||
def process_interfaces(self, filedata):
|
||||
|
||||
# process line continuations
|
||||
filedata = ' '.join(d.strip() for d in filedata.split('\\'))
|
||||
|
||||
line_idx = 0
|
||||
lines_consumed = 0
|
||||
raw_config = filedata.split('\n')
|
||||
lines = [l.strip(whitespaces) for l in raw_config]
|
||||
while (line_idx < len(lines)):
|
||||
if self.ignore_line(lines[line_idx]):
|
||||
line_idx += 1
|
||||
continue
|
||||
words = re.split(self._ws_split_regex, lines[line_idx])
|
||||
if not words:
|
||||
line_idx += 1
|
||||
continue
|
||||
# Check if first element is a supported keyword
|
||||
if self._is_keyword(words[0]):
|
||||
keyword_func = self._get_keyword_func(words[0])
|
||||
lines_consumed = keyword_func(self, lines, line_idx, line_idx+1)
|
||||
line_idx += lines_consumed
|
||||
else:
|
||||
self._parse_error(self._currentfile, line_idx + 1,
|
||||
'error processing line \'%s\'' %lines[line_idx])
|
||||
line_idx += 1
|
||||
return 0
|
||||
|
||||
def read_filedata(self, filedata):
|
||||
self._currentfile_has_template = False
|
||||
# run through template engine
|
||||
try:
|
||||
rendered_filedata = self._template_engine.render(filedata)
|
||||
if rendered_filedata is filedata:
|
||||
self._currentfile_has_template = False
|
||||
else:
|
||||
self._currentfile_has_template = True
|
||||
except Exception as e:
|
||||
self._parse_error(self._currentfile, -1,
|
||||
'failed to render template (%s). ' %str(e) +
|
||||
'Continue without template rendering ...')
|
||||
rendered_filedata = None
|
||||
pass
|
||||
if rendered_filedata:
|
||||
self.process_interfaces(rendered_filedata)
|
||||
else:
|
||||
self.process_interfaces(filedata)
|
||||
|
||||
def read_file(self, filename, fileiobuf=None):
|
||||
if fileiobuf:
|
||||
self.read_filedata(fileiobuf)
|
||||
return
|
||||
self._filestack.append(filename)
|
||||
self.logger.info('processing interfaces file %s' %filename)
|
||||
f = open(filename)
|
||||
filedata = f.read()
|
||||
f.close()
|
||||
self.read_filedata(filedata)
|
||||
self._filestack.pop()
|
||||
|
||||
def read_file_json(self, filename, fileiobuf=None):
|
||||
if fileiobuf:
|
||||
ifacedicts = json.loads(fileiobuf, encoding="utf-8")
|
||||
#object_hook=ifaceJsonDecoder.json_object_hook)
|
||||
elif filename:
|
||||
self.logger.info('processing interfaces file %s' %filename)
|
||||
fp = open(filename)
|
||||
ifacedicts = json.load(fp)
|
||||
#object_hook=ifaceJsonDecoder.json_object_hook)
|
||||
|
||||
# we need to handle both lists and non lists formats (e.g. {{}})
|
||||
if not isinstance(ifacedicts,list):
|
||||
ifacedicts = [ifacedicts]
|
||||
|
||||
for ifacedict in ifacedicts:
|
||||
ifaceobj = ifaceJsonDecoder.json_to_ifaceobj(ifacedict)
|
||||
if ifaceobj:
|
||||
self._validate_addr_family(ifaceobj)
|
||||
self.callbacks.get('validateifaceobj')(ifaceobj)
|
||||
self.callbacks.get('iface_found')(ifaceobj)
|
||||
|
||||
def load(self):
|
||||
""" This member function loads the networkinterfaces file.
|
||||
|
||||
Assumes networkinterfaces parser object is initialized with the
|
||||
parser arguments
|
||||
"""
|
||||
if self.interfacesfileformat == 'json':
|
||||
return self.read_file_json(self.interfacesfile,
|
||||
self.interfacesfileiobuf)
|
||||
return self.read_file(self.interfacesfile,
|
||||
self.interfacesfileiobuf)
|
175
ifupdown/policymanager.py
Normal file
175
ifupdown/policymanager.py
Normal file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2015 Cumulus Networks, Inc. All rights reserved.
|
||||
#
|
||||
#
|
||||
'''
|
||||
The PolicyManager should be subclassed by addon modules
|
||||
to read a JSON policy config file that is later used to
|
||||
set defaults:
|
||||
|
||||
Initialize: This module defines a list of config file location based
|
||||
on module. There are defined in the __init__(): All the
|
||||
addon modules need to do is import the policymanager module.
|
||||
|
||||
import ifupdown.policymanager as policymanager
|
||||
|
||||
|
||||
Provides: an API to retrieve link attributes based on addon module name,
|
||||
interface name, and attribute.
|
||||
|
||||
The ifupdown.policymanager module provides a global object policymanager_api
|
||||
that can be called like so:
|
||||
|
||||
speed_default = policymanager.policymanager_api.get_default(
|
||||
module_name='ethtool',
|
||||
ifname=ifaceobj.name,
|
||||
attr='link-speed'
|
||||
)
|
||||
'''
|
||||
|
||||
import json
|
||||
import logging
|
||||
import glob
|
||||
|
||||
class policymanager():
|
||||
def __init__(self):
|
||||
# we should check for these files in order
|
||||
# so that customers can override the /var/lib file settings
|
||||
self.logger = logging.getLogger('ifupdown.' +
|
||||
self.__class__.__name__)
|
||||
|
||||
# we grab the json files from a known location and make sure that
|
||||
# the defaults_policy is checked first
|
||||
user_files = glob.glob('/etc/network/ifupdown2/policy.d/*.json')
|
||||
# grab the default module files
|
||||
default_files = glob.glob('/var/lib/ifupdownaddons/policy.d/*.json')
|
||||
# keep an array of defaults indexed by module name
|
||||
self.system_policy_array = {}
|
||||
for filename in default_files:
|
||||
system_array = {}
|
||||
try:
|
||||
fd = open(filename,'r')
|
||||
system_array = json.load(fd)
|
||||
self.logger.debug('reading %s system policy defaults config' \
|
||||
% filename)
|
||||
except Exception as e:
|
||||
self.logger.debug('could not read %s system policy defaults config' \
|
||||
% filename)
|
||||
self.logger.debug(' exception is %s' % str(e))
|
||||
for module in system_array.keys():
|
||||
if self.system_policy_array.has_key(module):
|
||||
self.logger.debug('warning: overwriting system module %s from file %s' \
|
||||
% (module,filename))
|
||||
self.system_policy_array[module] = system_array[module]
|
||||
|
||||
# take care of user defined policy defaults
|
||||
self.user_policy_array = {}
|
||||
for filename in user_files:
|
||||
user_array = {}
|
||||
try:
|
||||
fd = open(filename,'r')
|
||||
user_array = json.load(fd)
|
||||
self.logger.debug('reading %s policy user defaults config' \
|
||||
% filename)
|
||||
except Exception as e:
|
||||
self.logger.debug('could not read %s user policy defaults config' \
|
||||
% filename)
|
||||
self.logger.debug(' exception is %s' % str(e))
|
||||
# customer added module attributes
|
||||
for module in user_array.keys():
|
||||
if self.system_policy_array.has_key(module):
|
||||
# warn user that we are overriding the system module setting
|
||||
self.logger.debug('warning: overwriting system with user module %s from file %s' \
|
||||
% (module,filename))
|
||||
self.user_policy_array[module] = user_array[module]
|
||||
return
|
||||
|
||||
def get_iface_default(self,module_name=None,ifname=None,attr=None):
|
||||
'''
|
||||
get_iface_default: Addon modules must use one of two types of access methods to
|
||||
the default configs. In this method, we expect the default to be
|
||||
either in
|
||||
[module]['iface_defaults'][ifname][attr] or
|
||||
[module]['defaults'][attr]
|
||||
We first check the user_policy_array and return that value. But if
|
||||
the user did not specify an override, we use the system_policy_array.
|
||||
'''
|
||||
# make sure we have an index
|
||||
if (not ifname or not attr or not module_name):
|
||||
return None
|
||||
|
||||
val = None
|
||||
# users can specify defaults to override the systemwide settings
|
||||
# look for user specific interface attribute iface_defaults first
|
||||
try:
|
||||
# looks for user specified value
|
||||
val = self.user_policy_array[module_name]['iface_defaults'][ifname][attr]
|
||||
return val
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
# failing that, there may be a user default for all intefaces
|
||||
val = self.user_policy_array[module_name]['defaults'][attr]
|
||||
return val
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
# failing that, look for system setting for the interface
|
||||
val = self.system_policy_array[module_name]['iface_defaults'][ifname][attr]
|
||||
return val
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
# failing that, look for system setting for all interfaces
|
||||
val = self.system_policy_array[module_name]['defaults'][attr]
|
||||
return val
|
||||
except:
|
||||
pass
|
||||
|
||||
# could not find any system or user default so return Non
|
||||
return val
|
||||
|
||||
def get_attr_default(self,module_name=None,attr=None):
|
||||
'''
|
||||
get_attr_default: Addon modules must use one of two types of access methods to
|
||||
the default configs. In this method, we expect the default to be in
|
||||
|
||||
[module][attr]
|
||||
|
||||
We first check the user_policy_array and return that value. But if
|
||||
the user did not specify an override, we use the system_policy_array.
|
||||
'''
|
||||
if (not attr or not module_name):
|
||||
return None
|
||||
# users can specify defaults to override the systemwide settings
|
||||
# look for user specific interface attribute iface_defaults first
|
||||
val = None
|
||||
if self.user_policy_array.get(module_name):
|
||||
val = self.user_policy_array[module_name].get(attr)
|
||||
|
||||
if not val:
|
||||
if self.system_policy_array.get(module_name):
|
||||
val = self.system_policy_array[module_name].get(attr)
|
||||
|
||||
return val
|
||||
|
||||
def get_module_default(self,module_name=None):
|
||||
'''
|
||||
get_module_default: Addon modules can also access the entire config
|
||||
This method returns indexed by "system" and "user": these are the
|
||||
system-wide and user-defined policy arrays for a specific module.
|
||||
'''
|
||||
if not module_name:
|
||||
return None
|
||||
if self.system_policy_array.get(module_name) and \
|
||||
self.user_policy_array.get(module_name):
|
||||
mod_array = {"system":self.system_policy_array[module_name],
|
||||
"user":self.user_policy_array[module_name]}
|
||||
else:
|
||||
# the module must not have these defined, return None
|
||||
mod_array = None
|
||||
|
||||
return mod_array
|
||||
|
||||
policymanager_api = policymanager()
|
859
ifupdown/rtnetlink.py
Normal file
859
ifupdown/rtnetlink.py
Normal file
@@ -0,0 +1,859 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
#
|
||||
# Author: Scott Feldman, sfeldma@cumulusnetworks.com
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
#
|
||||
from socket import NETLINK_ROUTE, AF_INET, AF_INET6
|
||||
from string import printable
|
||||
from ipaddr import *
|
||||
from ctypes import *
|
||||
from netlink import *
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
#
|
||||
# from /usr/include/linux/rtnetlink.h
|
||||
#
|
||||
|
||||
RTMGRP_LINK = 0x1
|
||||
RTMGRP_IPV4_IFADDR = 0x10
|
||||
RTMGRP_IPV4_ROUTE = 0x40
|
||||
RTMGRP_IPV6_IFADDR = 0x100
|
||||
RTMGRP_IPV6_ROUTE = 0x400
|
||||
|
||||
RTM_NEWLINK = 16
|
||||
RTM_DELLINK = 17
|
||||
RTM_GETLINK = 18
|
||||
RTM_SETLINK = 19
|
||||
RTM_NEWADDR = 20
|
||||
RTM_DELADDR = 21
|
||||
RTM_GETADDR = 22
|
||||
RTM_NEWROUTE = 24
|
||||
RTM_DELROUTE = 25
|
||||
RTM_GETROUTE = 26
|
||||
|
||||
# Definitions used in routing table administration.
|
||||
|
||||
class Nlmsg(Structure):
|
||||
|
||||
def _stringify(self):
|
||||
return string_at(addressof(self), sizeof(self))
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._stringify() == other._stringify() and \
|
||||
self.__dict__ == other.__dict__
|
||||
|
||||
def to_rta(self):
|
||||
return Rtattr.from_address(addressof(self) + NLMSG_ALIGN(sizeof(self)))
|
||||
|
||||
def pack_extra(self, extra, addr):
|
||||
memmove(addr, addressof(extra), sizeof(extra))
|
||||
return NLMSG_ALIGN(sizeof(extra))
|
||||
|
||||
def pack_rtas(self, rtas, addr):
|
||||
total_len = 0
|
||||
for rta_type, value in rtas.iteritems():
|
||||
rta = Rtattr.from_address(addr)
|
||||
rta.rta_type = rta_type
|
||||
pack_fn = self.rta_fn(rta_type)
|
||||
rta_len = NLMSG_ALIGN(pack_fn(rta, value))
|
||||
total_len += rta_len
|
||||
addr += rta_len
|
||||
return total_len
|
||||
|
||||
def pack_rtas_new(self, rtas, addr, policy):
|
||||
total_len = 0
|
||||
|
||||
for rta_type, value in rtas.iteritems():
|
||||
if type(value) == dict:
|
||||
rta = Rtattr.from_address(addr)
|
||||
rta.rta_type = rta_type
|
||||
rta.rta_len = RTA_LENGTH(0)
|
||||
rta_len = NLMSG_ALIGN(rta.rta_len)
|
||||
total_len += rta_len
|
||||
addr += rta_len
|
||||
pack_fn = policy.get(rta_type)
|
||||
rta_len = NLMSG_ALIGN(pack_fn(addr, value))
|
||||
rta.rta_len += rta_len
|
||||
else:
|
||||
rta = Rtattr.from_address(addr)
|
||||
rta.rta_type = rta_type
|
||||
pack_fn = policy.get(rta_type)
|
||||
rta_len = NLMSG_ALIGN(pack_fn(rta, value))
|
||||
total_len += rta_len
|
||||
addr += rta_len
|
||||
return total_len
|
||||
|
||||
def rta_linkinfo(self, addr, rtas):
|
||||
total_len = 0
|
||||
|
||||
# Check interface kind
|
||||
kind = rtas.get(IFLA_INFO_KIND)
|
||||
if kind == 'vlan':
|
||||
data_policy = self.rta_linkinfo_data_vlan_policy()
|
||||
else:
|
||||
data_policy = self.rta_linkinfo_data_macvlan_policy()
|
||||
|
||||
# Pack info kind
|
||||
rta = Rtattr.from_address(addr)
|
||||
rta.rta_type = IFLA_INFO_KIND
|
||||
rta_len = NLMSG_ALIGN(self.rta_string(rta, kind))
|
||||
total_len += rta_len
|
||||
addr += rta_len
|
||||
|
||||
# nest start link info data
|
||||
rta = Rtattr.from_address(addr)
|
||||
rta.rta_type = IFLA_INFO_DATA
|
||||
rta.rta_len = RTA_LENGTH(0)
|
||||
rta_len = NLMSG_ALIGN(rta.rta_len)
|
||||
total_len += rta_len
|
||||
addr += rta_len
|
||||
rta_len = self.pack_rtas_new(rtas.get(IFLA_INFO_DATA), addr,
|
||||
data_policy)
|
||||
rta.rta_len += rta_len
|
||||
|
||||
total_len += rta_len
|
||||
addr += rta_len
|
||||
|
||||
return total_len
|
||||
|
||||
def rta_bridge_vlan_info(self, rta, value):
|
||||
if value:
|
||||
data = RTA_DATA(rta)
|
||||
memmove(data, addressof(value), sizeof(value))
|
||||
rta.rta_len = RTA_LENGTH(sizeof(value))
|
||||
return rta.rta_len
|
||||
|
||||
def rta_af_spec(self, addr, rtas):
|
||||
total_len = 0
|
||||
|
||||
# XXX: Check family (Assumes bridge family for now)
|
||||
rta_len = self.pack_rtas_new(rtas, addr,
|
||||
self.rta_bridge_af_spec_policy())
|
||||
total_len += rta_len
|
||||
return total_len
|
||||
|
||||
def unpack_rtas(self, which_ones=[]):
|
||||
len = self.nlh.nlmsg_len - NLMSG_LENGTH(sizeof(self))
|
||||
rta = self.to_rta()
|
||||
rtas = {}
|
||||
while RTA_OK(rta, len):
|
||||
rta_type = rta.rta_type
|
||||
if not which_ones or rta_type in which_ones:
|
||||
unpack_fn = self.rta_fn(rta_type)
|
||||
rtas[rta_type] = unpack_fn(rta)
|
||||
len, rta = RTA_NEXT(rta, len)
|
||||
return rtas
|
||||
|
||||
def dump_rtas(self):
|
||||
rtas = self.unpack_rtas()
|
||||
for type, value in rtas.iteritems():
|
||||
print ("rta", type, ":", value)
|
||||
|
||||
class _IPv6Addr(BigEndianStructure):
|
||||
_fields_ = [
|
||||
('upper', c_uint64),
|
||||
('lower', c_uint64),
|
||||
]
|
||||
|
||||
class _IPv4Addr(BigEndianStructure):
|
||||
_fields_ = [
|
||||
('addr', c_uint32),
|
||||
]
|
||||
|
||||
def rta_uint8(self, rta, value=None):
|
||||
data = RTA_DATA(rta)
|
||||
if value:
|
||||
c_uint8.from_address(data).value = value
|
||||
rta.rta_len = RTA_LENGTH(sizeof(c_uint8))
|
||||
return rta.rta_len
|
||||
else:
|
||||
return c_uint8.from_address(data).value
|
||||
|
||||
def rta_uint16(self, rta, value=None):
|
||||
data = RTA_DATA(rta)
|
||||
if value:
|
||||
c_uint16.from_address(data).value = value
|
||||
rta.rta_len = RTA_LENGTH(sizeof(c_uint16))
|
||||
return rta.rta_len
|
||||
else:
|
||||
return c_uint16.from_address(data).value
|
||||
|
||||
def rta_uint32(self, rta, value=None):
|
||||
data = RTA_DATA(rta)
|
||||
if value:
|
||||
c_uint32.from_address(data).value = value
|
||||
rta.rta_len = RTA_LENGTH(sizeof(c_uint32))
|
||||
return rta.rta_len
|
||||
else:
|
||||
return c_uint32.from_address(data).value
|
||||
|
||||
def rta_string(self, rta, value=None):
|
||||
data = RTA_DATA(rta)
|
||||
if value:
|
||||
s = create_string_buffer(value)
|
||||
memmove(data, addressof(s), len(value))
|
||||
rta.rta_len = RTA_LENGTH(len(value))
|
||||
return rta.rta_len
|
||||
else:
|
||||
return c_char_p(data).value
|
||||
|
||||
def rta_addr(self, rta, value=None):
|
||||
data = RTA_DATA(rta)
|
||||
if value:
|
||||
if isinstance(value, IPv4Address):
|
||||
self._IPv4Addr.from_address(data).addr = value._ip
|
||||
rta.rta_len = RTA_LENGTH(sizeof(self._IPv4Addr))
|
||||
elif isinstance(value, IPv6Address):
|
||||
addr = self._IPv6Addr.from_address(data)
|
||||
addr.upper = value._ip >> 64
|
||||
addr.lower = value._ip & 0xffffffffffffffff
|
||||
rta.rta_len = RTA_LENGTH(sizeof(self._IPv6Addr))
|
||||
else:
|
||||
assert(False)
|
||||
return rta.rta_len
|
||||
else:
|
||||
if RTA_PAYLOAD(rta) == 4:
|
||||
addr = c_uint32.__ctype_be__.from_address(data).value
|
||||
addr = IPv4Address(addr)
|
||||
else:
|
||||
addr = self._IPv6Addr.from_address(data)
|
||||
addr = IPv6Address((addr.upper << 64) + addr.lower)
|
||||
return addr
|
||||
|
||||
def rta_uint8_array(self, rta, value=None):
|
||||
data = RTA_DATA(rta)
|
||||
if value:
|
||||
s = (c_uint8 * len(value)).from_buffer_copy(value)
|
||||
memmove(data, addressof(s), len(value))
|
||||
rta.rta_len = RTA_LENGTH(len(value))
|
||||
return rta.rta_len
|
||||
else:
|
||||
array = (c_uint8 * RTA_PAYLOAD(rta))()
|
||||
memmove(array, data, RTA_PAYLOAD(rta))
|
||||
return array
|
||||
|
||||
def rta_uint32_array(self, rta, value=None):
|
||||
if value:
|
||||
assert(False)
|
||||
else:
|
||||
data = RTA_DATA(rta)
|
||||
size = RTA_PAYLOAD(rta) / sizeof(c_uint32)
|
||||
array = (c_uint32 * size)()
|
||||
memmove(array, data, RTA_PAYLOAD(rta))
|
||||
return array
|
||||
|
||||
def rta_multipath(self, rta, value=None):
|
||||
# XXX implement this
|
||||
return None
|
||||
|
||||
def rta_wtf(self, rta, value=None):
|
||||
return None
|
||||
|
||||
def rta_none(self, rta, value=None):
|
||||
return None
|
||||
|
||||
def rta_fn(self, rta_type):
|
||||
return None
|
||||
|
||||
|
||||
# rtm_type
|
||||
|
||||
RTN_UNSPEC = 0
|
||||
RTN_UNICAST = 1 # Gateway or direct route
|
||||
RTN_LOCAL = 2 # Accept locally
|
||||
RTN_BROADCAST = 3 # Accept locally as broadcast,
|
||||
# send as broadcast
|
||||
RTN_ANYCAST = 4 # Accept locally as broadcast,
|
||||
# but send as unicast
|
||||
RTN_MULTICAST = 5 # Multicast route
|
||||
RTN_BLACKHOLE = 6 # Drop
|
||||
RTN_UNREACHABLE = 7 # Destination is unreachable
|
||||
RTN_PROHIBIT = 8 # Administratively prohibited
|
||||
RTN_THROW = 9 # Not in this table
|
||||
RTN_NAT = 10 # Translate this address
|
||||
RTN_XRESOLVE = 11 # Use external resolver
|
||||
RTN_MAX = 11
|
||||
|
||||
# rtm_protocol
|
||||
|
||||
RTPROT_UNSPEC = 0
|
||||
RTPROT_REDIRECT = 1 # Route installed by ICMP redirects;
|
||||
# not used by current IPv4
|
||||
RTPROT_KERNEL = 2 # Route installed by kernel
|
||||
RTPROT_BOOT = 3 # Route installed during boot
|
||||
RTPROT_STATIC = 4 # Route installed by administrator
|
||||
|
||||
# Values of protocol >= RTPROT_STATIC are not interpreted by kernel;
|
||||
# they are just passed from user and back as is.
|
||||
# It will be used by hypothetical multiple routing daemons.
|
||||
# Note that protocol values should be standardized in order to
|
||||
# avoid conflicts.
|
||||
|
||||
RTPROT_GATED = 8 # Apparently, GateD
|
||||
RTPROT_RA = 9 # RDISC/ND router advertisements
|
||||
RTPROT_MRT = 10 # Merit MRT
|
||||
RTPROT_ZEBRA = 11 # Zebra
|
||||
RTPROT_BIRD = 12 # BIRD
|
||||
RTPROT_DNROUTED = 13 # DECnet routing daemon
|
||||
RTPROT_XORP = 14 # XORP
|
||||
RTPROT_NTK = 15 # Netsukuku
|
||||
RTPROT_DHCP = 16 # DHCP client
|
||||
|
||||
# rtm_scope
|
||||
|
||||
# Really it is not scope, but sort of distance to the destination.
|
||||
# NOWHERE are reserved for not existing destinations, HOST is our
|
||||
# local addresses, LINK are destinations, located on directly attached
|
||||
# link and UNIVERSE is everywhere in the Universe.
|
||||
|
||||
# Intermediate values are also possible f.e. interior routes
|
||||
# could be assigned a value between UNIVERSE and LINK.
|
||||
|
||||
RT_SCOPE_UNIVERSE = 0
|
||||
# User defined values
|
||||
RT_SCOPE_SITE = 200
|
||||
RT_SCOPE_LINK = 253
|
||||
RT_SCOPE_HOST = 254
|
||||
RT_SCOPE_NOWHERE=255
|
||||
|
||||
# rtm_flags
|
||||
|
||||
RTM_F_NOTIFY = 0x100 # Notify user of route change
|
||||
RTM_F_CLONED = 0x200 # This route is cloned
|
||||
RTM_F_EQUALIZE = 0x400 # Multipath equalizer: NI
|
||||
RTM_F_PREFIX = 0x800 # Prefix addresses
|
||||
|
||||
# Reserved table identifiers
|
||||
|
||||
RT_TABLE_UNSPEC = 0
|
||||
# User defined values
|
||||
RT_TABLE_COMPAT = 252
|
||||
RT_TABLE_DEFAULT = 253
|
||||
RT_TABLE_MAIN = 254
|
||||
RT_TABLE_LOCAL = 255
|
||||
RT_TABLE_MAX = 0xFFFFFFFF
|
||||
|
||||
# Generic structure for encapsulation of optional route information.
|
||||
# It is reminiscent of sockaddr, but with sa_family replaced
|
||||
# with attribute type.
|
||||
|
||||
class Rtattr(Structure):
|
||||
|
||||
_fields_ = [
|
||||
('rta_len', c_uint16),
|
||||
('rta_type', c_uint16),
|
||||
]
|
||||
|
||||
# Routing message attributes
|
||||
|
||||
RTA_UNSPEC = 0
|
||||
RTA_DST = 1
|
||||
RTA_SRC = 2
|
||||
RTA_IIF = 3
|
||||
RTA_OIF = 4
|
||||
RTA_GATEWAY = 5
|
||||
RTA_PRIORITY = 6
|
||||
RTA_PREFSRC = 7
|
||||
RTA_METRICS = 8
|
||||
RTA_MULTIPATH = 9
|
||||
RTA_PROTOINFO = 10 # no longer used
|
||||
RTA_FLOW = 11
|
||||
RTA_CACHEINFO = 12
|
||||
RTA_SESSION = 13 # no longer used
|
||||
RTA_MP_ALGO = 14 # no longer used
|
||||
RTA_TABLE = 15
|
||||
RTA_MAX = 15
|
||||
|
||||
# Macros to handle rtattributes
|
||||
|
||||
RTA_ALIGNTO = 4
|
||||
def RTA_ALIGN(len):
|
||||
return (len + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)
|
||||
def RTA_OK(rta, len):
|
||||
return len >= sizeof(Rtattr) and \
|
||||
rta.rta_len >= sizeof(Rtattr) and \
|
||||
rta.rta_len <= len
|
||||
def RTA_NEXT(rta, len):
|
||||
cur = RTA_ALIGN(rta.rta_len)
|
||||
rta = Rtattr.from_address(addressof(rta) + cur)
|
||||
return len - cur, rta
|
||||
def RTA_LENGTH(len):
|
||||
return len + RTA_ALIGN(sizeof(Rtattr))
|
||||
def RTA_SPACE(len):
|
||||
return RTA_ALIGN(RTA_LENGTH(len))
|
||||
def RTA_DATA(rta):
|
||||
return addressof(rta) + RTA_LENGTH(0)
|
||||
def RTA_PAYLOAD(rta):
|
||||
return rta.rta_len - RTA_LENGTH(0)
|
||||
|
||||
RTNH_F_DEAD = 1 # Nexthop is dead (used by multipath)
|
||||
RTNH_F_PERVASIVE = 2 # Do recursive gateway lookup
|
||||
RTNH_F_ONLINK = 4 # Gateway is forced on link
|
||||
|
||||
# Reserved table identifiers
|
||||
|
||||
RT_TABLE_UNSPEC = 0
|
||||
# User defined values
|
||||
RT_TABLE_COMPAT = 252
|
||||
RT_TABLE_DEFAULT = 253
|
||||
RT_TABLE_MAIN = 254
|
||||
RT_TABLE_LOCAL = 255
|
||||
RT_TABLE_MAX = 0xFFFFFFFF
|
||||
|
||||
class Rtmsg(Nlmsg):
|
||||
|
||||
_fields_ = [
|
||||
('rtm_family', c_uint8),
|
||||
('rtm_dst_len', c_uint8),
|
||||
('rtm_src_len', c_uint8),
|
||||
('rtm_tos', c_uint8),
|
||||
('rtm_table', c_uint8),
|
||||
('rtm_protocol', c_uint8),
|
||||
('rtm_scope', c_uint8),
|
||||
('rtm_type', c_uint8),
|
||||
('rtm_flags', c_uint32),
|
||||
]
|
||||
|
||||
_table_str = {
|
||||
RT_TABLE_UNSPEC: "unspecified",
|
||||
RT_TABLE_COMPAT: "compat",
|
||||
RT_TABLE_DEFAULT: "default",
|
||||
RT_TABLE_MAIN: "main",
|
||||
RT_TABLE_LOCAL: "local",
|
||||
}
|
||||
|
||||
_proto_str = {
|
||||
RTPROT_UNSPEC: "none",
|
||||
RTPROT_REDIRECT: "redirect",
|
||||
RTPROT_KERNEL: "kernel",
|
||||
RTPROT_BOOT: "boot",
|
||||
RTPROT_STATIC: "static",
|
||||
RTPROT_GATED: "gated",
|
||||
RTPROT_RA: "ra",
|
||||
RTPROT_MRT: "mrtmrt",
|
||||
RTPROT_ZEBRA: "zebra",
|
||||
RTPROT_BIRD: "bird",
|
||||
RTPROT_DNROUTED: "dnrouted",
|
||||
RTPROT_XORP: "xorp",
|
||||
RTPROT_NTK: "ntk",
|
||||
RTPROT_DHCP: "dhcp",
|
||||
}
|
||||
|
||||
_scope_str = {
|
||||
RT_SCOPE_UNIVERSE: "universe",
|
||||
RT_SCOPE_SITE: "site",
|
||||
RT_SCOPE_LINK: "link",
|
||||
RT_SCOPE_HOST: "host",
|
||||
RT_SCOPE_NOWHERE: "nowhere",
|
||||
}
|
||||
|
||||
_type_str = {
|
||||
RTN_UNSPEC: "unspecified",
|
||||
RTN_UNICAST: "unicast",
|
||||
RTN_LOCAL: "local",
|
||||
RTN_BROADCAST: "broadcast",
|
||||
RTN_ANYCAST: "anycast",
|
||||
RTN_MULTICAST: "multicast",
|
||||
RTN_BLACKHOLE: "blackhole",
|
||||
RTN_UNREACHABLE: "unreachable",
|
||||
RTN_PROHIBIT: "prohibit",
|
||||
RTN_THROW: "throw",
|
||||
RTN_NAT: "nat",
|
||||
RTN_XRESOLVE: "xresolve",
|
||||
}
|
||||
|
||||
def dump(self):
|
||||
print ('rtm_family', self.rtm_family)
|
||||
print ('rtm_dst_len', self.rtm_dst_len)
|
||||
print ('rtm_src_len', self.rtm_src_len)
|
||||
print ('rtm_tos', self.rtm_tos)
|
||||
print ('rtm_table', self._table_str.get(self.rtm_table, self.rtm_table))
|
||||
print ('rtm_protocol', self._proto_str.get(self.rtm_protocol))
|
||||
print ('rtm_scope', self._scope_str.get(self.rtm_scope))
|
||||
print ('rtm_type', self._type_str.get(self.rtm_type))
|
||||
print ('rtm_flags 0x%08x' % self.rtm_flags)
|
||||
|
||||
def rta_fn(self, rta_type):
|
||||
fns = {
|
||||
RTA_DST: self.rta_addr,
|
||||
RTA_SRC: self.rta_addr,
|
||||
RTA_IIF: self.rta_uint32,
|
||||
RTA_OIF: self.rta_uint32,
|
||||
RTA_GATEWAY: self.rta_addr,
|
||||
RTA_PRIORITY: self.rta_uint32,
|
||||
RTA_PREFSRC: self.rta_addr,
|
||||
RTA_METRICS: self.rta_uint32_array,
|
||||
RTA_MULTIPATH: self.rta_multipath,
|
||||
RTA_PROTOINFO: self.rta_none,
|
||||
RTA_FLOW: self.rta_uint32,
|
||||
RTA_CACHEINFO: self.rta_none,
|
||||
RTA_SESSION: self.rta_none,
|
||||
RTA_MP_ALGO: self.rta_none,
|
||||
RTA_TABLE: self.rta_uint32,
|
||||
}
|
||||
|
||||
return fns.get(rta_type)
|
||||
|
||||
class Rtgenmsg(Nlmsg):
|
||||
|
||||
_fields_ = [
|
||||
('rtgen_family', c_uint8),
|
||||
]
|
||||
|
||||
def dump(self):
|
||||
print ('rtgen_family', self.rtgen_family)
|
||||
|
||||
# New extended info filters for IFLA_EXT_MASK
|
||||
RTEXT_FILTER_VF = (1 << 0)
|
||||
|
||||
# passes link level specific information, not dependent
|
||||
# on network protocol.
|
||||
|
||||
IFLA_UNSPEC = 0
|
||||
IFLA_ADDRESS = 1
|
||||
IFLA_BROADCAST = 2
|
||||
IFLA_IFNAME = 3
|
||||
IFLA_MTU = 4
|
||||
IFLA_LINK = 5
|
||||
IFLA_QDISC = 6
|
||||
IFLA_STATS = 7
|
||||
IFLA_COST = 8
|
||||
IFLA_PRIORITY = 9
|
||||
IFLA_MASTER = 10
|
||||
IFLA_WIRELESS = 11 # Wireless Extension event - see wireless.h
|
||||
IFLA_PROTINFO = 12 # Protocol specific information for a link
|
||||
IFLA_TXQLEN = 13
|
||||
IFLA_MAP = 14
|
||||
IFLA_WEIGHT = 15
|
||||
IFLA_OPERSTATE = 16
|
||||
IFLA_LINKMODE = 17
|
||||
IFLA_LINKINFO = 18
|
||||
IFLA_NET_NS_PID = 19
|
||||
IFLA_IFALIAS = 20
|
||||
IFLA_NUM_VF = 21 # Number of VFs if device is SR-IOV PF
|
||||
IFLA_VFINFO_LIST = 22
|
||||
IFLA_STATS64 = 23
|
||||
IFLA_VF_PORTS = 24
|
||||
IFLA_PORT_SELF = 25
|
||||
IFLA_AF_SPEC = 26
|
||||
IFLA_GROUP = 27 # Group the device belongs to
|
||||
IFLA_NET_NS_FD = 28
|
||||
IFLA_EXT_MASK = 29 # Extended info mask, VFs, etc
|
||||
IFLA_MAX = 29
|
||||
|
||||
|
||||
# IFLA_LINKINFO attributes
|
||||
IFLA_INFO_UNSPEC = 0
|
||||
IFLA_INFO_KIND = 1
|
||||
IFLA_INFO_DATA = 2
|
||||
IFLA_INFO_XSTATS = 3
|
||||
IFLA_INFO_MAX = 4
|
||||
|
||||
# IFLA_LINKINFO_DATA attributes for vlan
|
||||
IFLA_VLAN_UNSPEC = 0
|
||||
IFLA_VLAN_ID = 1
|
||||
|
||||
# IFLA_LINKINFO_DATA attributes for macvlan
|
||||
IFLA_MACVLAN_UNSPEC = 0
|
||||
IFLA_MACVLAN_MODE = 1
|
||||
|
||||
# macvlan modes
|
||||
MACVLAN_MODE_PRIVATE = 1
|
||||
MACVLAN_MODE_VEPA = 2
|
||||
MACVLAN_MODE_BRIDGE = 3
|
||||
MACVLAN_MODE_PASSTHRU = 4
|
||||
|
||||
# BRIDGE IFLA_AF_SPEC attributes
|
||||
IFLA_BRIDGE_FLAGS = 0
|
||||
IFLA_BRIDGE_MODE = 1
|
||||
IFLA_BRIDGE_VLAN_INFO = 2
|
||||
|
||||
# BRIDGE_VLAN_INFO flags
|
||||
BRIDGE_VLAN_INFO_MASTER = 1
|
||||
BRIDGE_VLAN_INFO_PVID = 2
|
||||
BRIDGE_VLAN_INFO_UNTAGGED = 4
|
||||
|
||||
# Bridge flags
|
||||
BRIDGE_FLAGS_MASTER = 1
|
||||
BRIDGE_FLAGS_SELF = 2
|
||||
|
||||
class BridgeVlanInfo(Structure):
|
||||
_fields_ = [
|
||||
('flags', c_uint16),
|
||||
('vid', c_uint16),
|
||||
('vid_end', c_uint16),
|
||||
]
|
||||
|
||||
class Ifinfomsg(Nlmsg):
|
||||
|
||||
_fields_ = [
|
||||
('ifi_family', c_uint8),
|
||||
('__ifi_pad', c_uint8),
|
||||
('ifi_type', c_uint16), # ARPHRD_*
|
||||
('ifi_index', c_int32), # Link index
|
||||
('ifi_flags', c_uint32), # IFF_* flags
|
||||
('ifi_change', c_uint32), # IFF_* change mask
|
||||
]
|
||||
|
||||
def dump(self):
|
||||
print ('ifi_family', self.ifi_family)
|
||||
print ('ifi_type', self.ifi_type)
|
||||
print ('ifi_index', self.ifi_index)
|
||||
print ('ifi_flags 0x%08x' % self.ifi_flags)
|
||||
print ('ifi_change 0x%08x' % self.ifi_change)
|
||||
|
||||
def rta_linkinfo_data_vlan_policy(self):
|
||||
fns = {
|
||||
IFLA_VLAN_ID : self.rta_uint16,
|
||||
}
|
||||
return fns
|
||||
|
||||
def rta_linkinfo_data_macvlan_policy(self):
|
||||
fns = {
|
||||
IFLA_MACVLAN_MODE : self.rta_uint32,
|
||||
}
|
||||
return fns
|
||||
|
||||
def rta_linkinfo_policy(self):
|
||||
fns = {
|
||||
IFLA_INFO_KIND : self.rta_string,
|
||||
IFLA_INFO_DATA : self.rta_linkinfo_data,
|
||||
}
|
||||
return fns
|
||||
|
||||
def rta_bridge_af_spec_policy(self):
|
||||
# Assume bridge family for now
|
||||
fns = {
|
||||
IFLA_BRIDGE_FLAGS : self.rta_uint16,
|
||||
IFLA_BRIDGE_VLAN_INFO : self.rta_bridge_vlan_info,
|
||||
}
|
||||
return fns
|
||||
|
||||
def rta_policy(self):
|
||||
fns = {
|
||||
IFLA_UNSPEC: self.rta_wtf,
|
||||
IFLA_ADDRESS: self.rta_uint8_array,
|
||||
IFLA_BROADCAST: self.rta_uint8_array,
|
||||
IFLA_IFNAME: self.rta_string,
|
||||
IFLA_MTU: self.rta_uint32,
|
||||
IFLA_LINK: self.rta_uint32,
|
||||
IFLA_QDISC: self.rta_string,
|
||||
IFLA_STATS: self.rta_none,
|
||||
IFLA_COST: self.rta_none,
|
||||
IFLA_PRIORITY: self.rta_none,
|
||||
IFLA_MASTER: self.rta_uint32,
|
||||
IFLA_WIRELESS: self.rta_none,
|
||||
IFLA_PROTINFO: self.rta_none,
|
||||
IFLA_TXQLEN: self.rta_uint32,
|
||||
IFLA_MAP: self.rta_none,
|
||||
IFLA_WEIGHT: self.rta_uint32,
|
||||
IFLA_OPERSTATE: self.rta_uint8,
|
||||
IFLA_LINKMODE: self.rta_uint8,
|
||||
IFLA_LINKINFO: self.rta_linkinfo,
|
||||
IFLA_NET_NS_PID: self.rta_uint32,
|
||||
IFLA_IFALIAS: self.rta_string,
|
||||
IFLA_NUM_VF: self.rta_uint32,
|
||||
IFLA_VFINFO_LIST: self.rta_none,
|
||||
IFLA_STATS64: self.rta_none,
|
||||
IFLA_VF_PORTS: self.rta_none,
|
||||
IFLA_PORT_SELF: self.rta_none,
|
||||
IFLA_AF_SPEC: self.rta_af_spec,
|
||||
IFLA_GROUP: self.rta_none,
|
||||
IFLA_NET_NS_FD: self.rta_none,
|
||||
IFLA_EXT_MASK: self.rta_none,
|
||||
}
|
||||
return fns;
|
||||
|
||||
def rta_fn(self, rta_type):
|
||||
fns = {
|
||||
IFLA_UNSPEC: self.rta_wtf,
|
||||
IFLA_ADDRESS: self.rta_uint8_array,
|
||||
IFLA_BROADCAST: self.rta_uint8_array,
|
||||
IFLA_IFNAME: self.rta_string,
|
||||
IFLA_MTU: self.rta_uint32,
|
||||
IFLA_LINK: self.rta_uint32,
|
||||
IFLA_QDISC: self.rta_string,
|
||||
IFLA_STATS: self.rta_none,
|
||||
IFLA_COST: self.rta_none,
|
||||
IFLA_PRIORITY: self.rta_none,
|
||||
IFLA_MASTER: self.rta_uint32,
|
||||
IFLA_WIRELESS: self.rta_none,
|
||||
IFLA_PROTINFO: self.rta_none,
|
||||
IFLA_TXQLEN: self.rta_uint32,
|
||||
IFLA_MAP: self.rta_none,
|
||||
IFLA_WEIGHT: self.rta_uint32,
|
||||
IFLA_OPERSTATE: self.rta_uint8,
|
||||
IFLA_LINKMODE: self.rta_uint8,
|
||||
IFLA_LINKINFO: self.rta_linkinfo,
|
||||
IFLA_NET_NS_PID: self.rta_uint32,
|
||||
IFLA_IFALIAS: self.rta_string,
|
||||
IFLA_NUM_VF: self.rta_uint32,
|
||||
IFLA_VFINFO_LIST: self.rta_none,
|
||||
IFLA_STATS64: self.rta_none,
|
||||
IFLA_VF_PORTS: self.rta_none,
|
||||
IFLA_PORT_SELF: self.rta_none,
|
||||
IFLA_AF_SPEC: self.rta_af_spec,
|
||||
IFLA_GROUP: self.rta_none,
|
||||
IFLA_NET_NS_FD: self.rta_none,
|
||||
IFLA_EXT_MASK: self.rta_none,
|
||||
}
|
||||
return fns.get(rta_type)
|
||||
|
||||
# passes address specific information
|
||||
|
||||
# Important comment:
|
||||
# IFA_ADDRESS is prefix address, rather than local interface address.
|
||||
# It makes no difference for normally configured broadcast interfaces,
|
||||
# but for point-to-point IFA_ADDRESS is DESTINATION address,
|
||||
# local address is supplied in IFA_LOCAL attribute.
|
||||
|
||||
IFA_UNSPEC = 0
|
||||
IFA_ADDRESS = 1
|
||||
IFA_LOCAL = 2
|
||||
IFA_LABEL = 3
|
||||
IFA_BROADCAST = 4
|
||||
IFA_ANYCAST = 5
|
||||
IFA_CACHEINFO = 6
|
||||
IFA_MULTICAST = 7
|
||||
IFA_MAX = 7
|
||||
|
||||
class Ifaddrmsg(Nlmsg):
|
||||
|
||||
_fields_ = [
|
||||
('ifa_family', c_uint8),
|
||||
('ifa_prefixlen', c_uint8), # The prefix length
|
||||
('ifa_flags', c_uint8), # Flags
|
||||
('ifa_scope', c_uint8), # Address scope
|
||||
('ifa_index', c_uint32), # Link index
|
||||
]
|
||||
|
||||
_family_str = {
|
||||
AF_INET: "inet",
|
||||
AF_INET6: "inet6",
|
||||
}
|
||||
|
||||
def dump(self):
|
||||
print ('ifa_family', self.ifa_family)
|
||||
print ('ifa_prefixlen', self.ifa_prefixlen)
|
||||
print ('ifa_flags 0x%02x' % self.ifa_flags)
|
||||
print ('ifa_scope', self.ifa_scope)
|
||||
print ('ifa_index', self.ifa_index)
|
||||
|
||||
def rta_fn(self, rta_type):
|
||||
fns = {
|
||||
IFA_ADDRESS: self.rta_addr,
|
||||
IFA_LOCAL: self.rta_addr,
|
||||
IFA_LABEL: self.rta_string,
|
||||
IFA_BROADCAST: self.rta_addr,
|
||||
IFA_ANYCAST: self.rta_addr,
|
||||
IFA_CACHEINFO: self.rta_none,
|
||||
IFA_MULTICAST: self.rta_addr,
|
||||
}
|
||||
return fns.get(rta_type)
|
||||
|
||||
class RtNetlinkError(Exception):
|
||||
|
||||
def __init__(self, message):
|
||||
Exception.__init__(self, message)
|
||||
logger.error(message)
|
||||
|
||||
class RtNetlink(Netlink):
|
||||
|
||||
def __init__(self, pid):
|
||||
Netlink.__init__(self, pid, NETLINK_ROUTE)
|
||||
|
||||
_rt_nlmsg_type_str = {
|
||||
RTM_NEWROUTE: "RTM_NEWROUTE",
|
||||
RTM_DELROUTE: "RTM_DELROUTE",
|
||||
RTM_NEWLINK: "RTM_NEWLINK",
|
||||
RTM_SETLINK: "RTM_SETLINK",
|
||||
RTM_DELLINK: "RTM_DELLINK",
|
||||
RTM_GETLINK: "RTM_GETLINK",
|
||||
RTM_NEWADDR: "RTM_NEWADDR",
|
||||
RTM_DELADDR: "RTM_DELADDR",
|
||||
}
|
||||
|
||||
def _hexdump(self, buf):
|
||||
while buf:
|
||||
chunk = buf[:16]
|
||||
buf = buf[16:]
|
||||
nums = ["%02x" % c for c in chunk]
|
||||
txt = [chr(c) if chr(c) in printable[:-5] else '.' for c in chunk]
|
||||
print (" ".join(nums).ljust(48), "".join(txt))
|
||||
|
||||
def dump(self, nlh):
|
||||
nlmsg = self.nlmsg(nlh)
|
||||
print
|
||||
self._hexdump(self.sendbuf[:nlh.nlmsg_len])
|
||||
print
|
||||
nlh.dump()
|
||||
print
|
||||
nlmsg.dump()
|
||||
print
|
||||
nlmsg.dump_rtas()
|
||||
|
||||
def nlmsg(self, nlh):
|
||||
nlmsg_struct = {
|
||||
RTM_NEWROUTE: Rtmsg,
|
||||
RTM_DELROUTE: Rtmsg,
|
||||
RTM_GETROUTE: Rtmsg,
|
||||
RTM_NEWLINK: Ifinfomsg,
|
||||
RTM_SETLINK: Ifinfomsg,
|
||||
RTM_DELLINK: Ifinfomsg,
|
||||
RTM_GETLINK: Rtgenmsg,
|
||||
RTM_NEWADDR: Ifaddrmsg,
|
||||
RTM_DELADDR: Ifaddrmsg,
|
||||
RTM_GETADDR: Rtgenmsg,
|
||||
}
|
||||
nldata = NLMSG_DATA(nlh)
|
||||
nlmsg = nlmsg_struct[nlh.nlmsg_type].from_address(nldata)
|
||||
nlmsg.nlh = nlh
|
||||
return nlmsg
|
||||
|
||||
def _nl_cb(self, nlh):
|
||||
# print "nl cb", self._rt_nlmsg_type_str[nlh.nlmsg_type]
|
||||
|
||||
if nlh.nlmsg_type in self._cbs:
|
||||
|
||||
nlmsg = self.nlmsg(nlh)
|
||||
|
||||
# validate nl length
|
||||
if nlh.nlmsg_len - NLMSG_LENGTH(sizeof(nlmsg)) < 0:
|
||||
raise RtNetlinkError("invalid nl length")
|
||||
|
||||
self._cbs[nlh.nlmsg_type](nlh, nlmsg)
|
||||
|
||||
def bind(self, groups, cbs):
|
||||
self._cbs = cbs
|
||||
Netlink.bind(self, groups, self._nl_cb)
|
||||
|
||||
def request(self, nlmsg_type, flags, extra, rtas={}):
|
||||
|
||||
nlh = Nlmsghdr.from_buffer(self.sendbuf)
|
||||
nlh_p = addressof(nlh)
|
||||
|
||||
seq = self.seq
|
||||
pid = self.pid
|
||||
|
||||
nlh.nlmsg_len = NLMSG_HDRLEN()
|
||||
nlh.nlmsg_type = nlmsg_type
|
||||
nlh.nlmsg_flags = flags
|
||||
nlh.nlmsg_pid = pid
|
||||
nlh.nlmsg_seq = seq
|
||||
|
||||
nlmsg = self.nlmsg(nlh)
|
||||
|
||||
nlh.nlmsg_len += nlmsg.pack_extra(extra, nlh_p + nlh.nlmsg_len)
|
||||
nlh.nlmsg_len += nlmsg.pack_rtas_new(rtas, nlh_p + nlh.nlmsg_len,
|
||||
nlmsg.rta_policy())
|
||||
#self.dump(nlh)
|
||||
self.sendall(string_at(nlh_p, nlh.nlmsg_len))
|
||||
self.seq += 1
|
||||
|
||||
token = (pid, seq)
|
||||
return token
|
237
ifupdown/rtnetlink_api.py
Normal file
237
ifupdown/rtnetlink_api.py
Normal file
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
#
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
#
|
||||
|
||||
from os import getpid
|
||||
from socket import AF_UNSPEC
|
||||
from socket import AF_BRIDGE
|
||||
from iff import IFF_UP
|
||||
from rtnetlink import *
|
||||
import os
|
||||
import ifupdownmain
|
||||
|
||||
class rtnetlinkApi(RtNetlink):
|
||||
|
||||
bind_done = False
|
||||
|
||||
def __init__(self, pid):
|
||||
RtNetlink.__init__(self, pid)
|
||||
self.logger = logging.getLogger('ifupdown.' +
|
||||
self.__class__.__name__)
|
||||
self.bind(0, None)
|
||||
self.bind_done = True
|
||||
self.ifindexmap = {}
|
||||
|
||||
def do_bind(self):
|
||||
if self.bind_done:
|
||||
return True
|
||||
self.bind(0, None)
|
||||
self.bind_done = True
|
||||
|
||||
def get_ifindex(self, ifname):
|
||||
ifindex = self.ifindexmap.get(ifname)
|
||||
if not ifindex:
|
||||
with open('/sys/class/net/%s/ifindex' %ifname, 'r') as f:
|
||||
ifindex = int(f.read())
|
||||
self.ifindexmap[ifname] = ifindex
|
||||
return ifindex
|
||||
|
||||
def create_vlan(self, link, ifname, vlanid):
|
||||
self.logger.info('rtnetlink: creating vlan interface %s' %ifname)
|
||||
if ifupdownmain.ifupdownFlags.DRYRUN:
|
||||
return
|
||||
try:
|
||||
ifindex = self.get_ifindex(link)
|
||||
except Exception as e:
|
||||
raise Exception('cannot determine ifindex for link %s (%s)'
|
||||
%(link, str(e)))
|
||||
|
||||
ifm = Ifinfomsg(AF_UNSPEC)
|
||||
rtas = {IFLA_IFNAME: ifname,
|
||||
IFLA_LINK : ifindex,
|
||||
IFLA_LINKINFO : {
|
||||
IFLA_INFO_KIND : 'vlan',
|
||||
IFLA_INFO_DATA : {
|
||||
IFLA_VLAN_ID : vlanid,
|
||||
}
|
||||
}
|
||||
}
|
||||
token = self.request(RTM_NEWLINK,
|
||||
NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
|
||||
self.process_wait([token])
|
||||
|
||||
def create_macvlan(self, ifname, link, mode='private'):
|
||||
self.logger.info('rtnetlink: creating macvlan interface %s' %ifname)
|
||||
if ifupdownmain.ifupdownFlags.DRYRUN:
|
||||
return
|
||||
try:
|
||||
ifindex = self.get_ifindex(link)
|
||||
except Exception as e:
|
||||
raise Exception('cannot determine ifindex for link %s (%s)'
|
||||
%(link, str(e)))
|
||||
|
||||
ifm = Ifinfomsg(AF_UNSPEC)
|
||||
rtas = {IFLA_IFNAME: ifname,
|
||||
IFLA_LINK : ifindex,
|
||||
IFLA_LINKINFO : {
|
||||
IFLA_INFO_KIND : 'macvlan',
|
||||
IFLA_INFO_DATA : {
|
||||
IFLA_MACVLAN_MODE : MACVLAN_MODE_PRIVATE,
|
||||
}
|
||||
}
|
||||
}
|
||||
token = self.request(RTM_NEWLINK, NLM_F_CREATE | NLM_F_REQUEST |
|
||||
NLM_F_ACK, ifm, rtas)
|
||||
self.process_wait([token])
|
||||
|
||||
def link_set(self, ifname, state):
|
||||
flags = 0
|
||||
self.logger.info('rtnetlink: setting link %s %s' %(ifname, state))
|
||||
if ifupdownmain.ifupdownFlags.DRYRUN:
|
||||
return
|
||||
|
||||
if state == "up":
|
||||
flags |= IFF_UP
|
||||
else:
|
||||
flags &= ~IFF_UP
|
||||
|
||||
ifm = Ifinfomsg(AF_UNSPEC, ifi_change=IFF_UP, ifi_flags=flags)
|
||||
rtas = {IFLA_IFNAME: ifname}
|
||||
|
||||
token = self.request(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
|
||||
self.process_wait([token])
|
||||
|
||||
def link_set_hwaddress(self, ifname, hwaddress):
|
||||
flags = 0
|
||||
self.logger.info('rtnetlink: setting link hwaddress %s %s' %(ifname, hwaddress))
|
||||
if ifupdownmain.ifupdownFlags.DRYRUN:
|
||||
return
|
||||
|
||||
flags &= ~IFF_UP
|
||||
ifm = Ifinfomsg(AF_UNSPEC, ifi_change=IFF_UP)
|
||||
rtas = {IFLA_IFNAME: ifname,
|
||||
IFLA_ADDRESS : str(bytearray([int(a,16) for a in hwaddress.split(':')]))}
|
||||
|
||||
self.logger.info(rtas)
|
||||
|
||||
token = self.request(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
|
||||
self.process_wait([token])
|
||||
|
||||
def addr_add(self, ifname, address, broadcast=None, peer=None, scope=None,
|
||||
preferred_lifetime=None):
|
||||
self.logger.info('rtnetlink: setting address')
|
||||
if ifupdownmain.ifupdownFlags.DRYRUN:
|
||||
return
|
||||
|
||||
try:
|
||||
ifindex = self.get_ifindex(link)
|
||||
except Exception as e:
|
||||
raise Exception('cannot determine ifindex for link %s (%s)'
|
||||
%(link, str(e)))
|
||||
ifa_scope = RT_SCOPE_
|
||||
if scope:
|
||||
if scope == "universe":
|
||||
ifa_scope = RT_SCOPE_UNIVERSE
|
||||
elif scope == "site":
|
||||
ifa_scope = RT_SCOPE_SITE
|
||||
elif scope == "link":
|
||||
ifa_scope = RT_SCOPE_LINK
|
||||
elif scope == "host":
|
||||
ifa_scope = RT_SCOPE_HOST
|
||||
elif scope == "nowhere":
|
||||
ifa_scope = RT_SCOPE_NOWHERE
|
||||
rtas = {IFLA_ADDRESS: ifname}
|
||||
|
||||
ifa = Ifaddrmsg(AF_UNSPEC, ifa_scope=ifa_scope, ifa_index=ifindex)
|
||||
|
||||
token = self.request(RTM_NEWADDR, NLM_F_REQUEST | NLM_F_ACK, ifa, rtas)
|
||||
self.process_wait([token])
|
||||
|
||||
def link_set_many(self, ifname, ifattrs):
|
||||
_ifattr_to_rta_map = {'dev' : IFLA_NAME,
|
||||
'address' : IFLA_ADDRESS,
|
||||
'broadcast' : IFLA_BROADCAST,
|
||||
'mtu' : IFLA_MTU,
|
||||
'master' : IFLA_MASTER}
|
||||
flags = 0
|
||||
ifi_change = IFF_UP
|
||||
rtas = {}
|
||||
self.logger.info('rtnetlink: setting link %s %s' %(ifname, state))
|
||||
if ifupdownmain.ifupdownFlags.DRYRUN:
|
||||
return
|
||||
if not ifattrs:
|
||||
return
|
||||
state = ifattrs.get('state')
|
||||
if state == 'up':
|
||||
flags |= IFF_UP
|
||||
elif state == 'down':
|
||||
flags &= ~IFF_UP
|
||||
else:
|
||||
ifi_change = 0
|
||||
|
||||
if ifi_change:
|
||||
ifm = Ifinfomsg(AF_UNSPEC, ifi_change=IFF_UP, ifi_flags=flags)
|
||||
else:
|
||||
ifm = Ifinfomsg(AF_UNSPEC)
|
||||
|
||||
for attr, attrval in ifattrs.items():
|
||||
rta_attr = _ifattr_to_rta_map.get(attr)
|
||||
if rta_attr:
|
||||
if attr == 'hwaddress':
|
||||
rtas[rta_attr] = str(bytearray([int(a,16) for a in attrval.split(':')]))
|
||||
else:
|
||||
rtas[rta_attr] = attrval
|
||||
|
||||
token = self.request(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
|
||||
self.process_wait([token])
|
||||
|
||||
def bridge_vlan(self, add=True, vid=None, dev=None, pvid=False,
|
||||
untagged=False, master=True):
|
||||
flags = 0
|
||||
vflags = 0
|
||||
if not vid or not dev:
|
||||
return
|
||||
self.logger.info('rtnetlink: bridge vlan add vid %s %s %s dev %s %s'
|
||||
%(vid, 'untagged' if untagged else '',
|
||||
'pvid' if pvid else '', dev,
|
||||
'self' if self else ''))
|
||||
if ifupdownmain.ifupdownFlags.DRYRUN:
|
||||
return
|
||||
try:
|
||||
ifindex = self.get_ifindex(dev)
|
||||
except Exception as e:
|
||||
raise Exception('cannot determine ifindex for dev %s (%s)'
|
||||
%(dev, str(e)))
|
||||
if not master:
|
||||
flags = BRIDGE_FLAGS_SELF
|
||||
|
||||
if pvid:
|
||||
vflags = BRIDGE_VLAN_INFO_PVID
|
||||
vflags |= BRIDGE_VLAN_INFO_UNTAGGED
|
||||
elif untagged:
|
||||
vflags |= BRIDGE_VLAN_INFO_UNTAGGED
|
||||
|
||||
ifm = Ifinfomsg(AF_BRIDGE, ifi_index=ifindex)
|
||||
rtas = {IFLA_AF_SPEC: {
|
||||
IFLA_BRIDGE_FLAGS: flags,
|
||||
IFLA_BRIDGE_VLAN_INFO : BridgeVlanInfo(vflags, int(vid), int(vid))
|
||||
}
|
||||
}
|
||||
if add:
|
||||
token = self.request(RTM_SETLINK,
|
||||
NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
|
||||
else:
|
||||
token = self.request(RTM_DELLINK,
|
||||
NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
|
||||
self.process_wait([token])
|
||||
|
||||
def bridge_vlan_many(self, add=True, vids=[], dev=None, pvid=False,
|
||||
untagged=False, master=True):
|
||||
for v in vids:
|
||||
self.bridge_vlan_add(add, v, dev, ispvid, isuntagged, master)
|
||||
|
||||
rtnl_api = rtnetlinkApi(os.getpid())
|
531
ifupdown/scheduler.py
Normal file
531
ifupdown/scheduler.py
Normal file
@@ -0,0 +1,531 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# 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 *
|
||||
from sets import Set
|
||||
|
||||
class ifaceSchedulerFlags():
|
||||
""" Enumerates scheduler flags """
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
_STATE_CHECK = True
|
||||
|
||||
_SCHED_RETVAL = True
|
||||
|
||||
@classmethod
|
||||
def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv=None):
|
||||
""" Runs sub operation on an interface """
|
||||
ifacename = ifaceobj.name
|
||||
|
||||
if ifupdownobj.type and ifupdownobj.type != ifaceobj.type:
|
||||
return
|
||||
|
||||
if not ifupdownobj.ADDONS_ENABLE: return
|
||||
if op == 'query-checkcurr':
|
||||
query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj)
|
||||
# If not type bridge vlan and the object does not exist,
|
||||
# mark not found and return
|
||||
if (not ifupdownobj.link_exists(ifaceobj.name) and
|
||||
ifaceobj.type != ifaceType.BRIDGE_VLAN):
|
||||
query_ifaceobj.set_state_n_status(ifaceState.from_str(op),
|
||||
ifaceStatus.NOTFOUND)
|
||||
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,
|
||||
ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
|
||||
else:
|
||||
ifupdownobj.logger.debug(msg)
|
||||
m.run(ifaceobj, op,
|
||||
ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
|
||||
except Exception as e:
|
||||
if not ifupdownobj.ignore_error(str(e)):
|
||||
err = 1
|
||||
ifupdownobj.logger.warn(str(e))
|
||||
# Continue with rest of the modules
|
||||
pass
|
||||
finally:
|
||||
if err or ifaceobj.status == ifaceStatus.ERROR:
|
||||
ifaceobj.set_state_n_status(ifaceState.from_str(op),
|
||||
ifaceStatus.ERROR)
|
||||
if 'up' in op or 'down' in op:
|
||||
cls._SCHED_RETVAL = False
|
||||
else:
|
||||
# Mark success only if the interface was not already
|
||||
# marked with error
|
||||
status = (ifaceobj.status
|
||||
if ifaceobj.status == ifaceStatus.ERROR
|
||||
else ifaceStatus.SUCCESS)
|
||||
ifaceobj.set_state_n_status(ifaceState.from_str(op),
|
||||
status)
|
||||
|
||||
if ifupdownobj.config.get('addon_scripts_support', '0') == '1':
|
||||
# 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 as e:
|
||||
ifupdownobj.log_error(str(e))
|
||||
|
||||
@classmethod
|
||||
def run_iface_list_ops(cls, ifupdownobj, ifaceobjs, ops):
|
||||
""" Runs all operations on a list of interface
|
||||
configurations for the same interface
|
||||
"""
|
||||
|
||||
# minor optimization. If operation is 'down', proceed only
|
||||
# if interface exists in the system
|
||||
ifacename = ifaceobjs[0].name
|
||||
ifupdownobj.logger.info('%s: running ops ...' %ifacename)
|
||||
if ('down' in ops[0] and
|
||||
ifaceobjs[0].type != ifaceType.BRIDGE_VLAN and
|
||||
not ifupdownobj.link_exists(ifacename)):
|
||||
ifupdownobj.logger.debug('%s: does not exist' %ifacename)
|
||||
# run posthook before you get out of here, so that
|
||||
# appropriate cleanup is done
|
||||
posthookfunc = ifupdownobj.sched_hooks.get('posthook')
|
||||
if posthookfunc:
|
||||
for ifaceobj in ifaceobjs:
|
||||
ifaceobj.status = ifaceStatus.SUCCESS
|
||||
posthookfunc(ifupdownobj, ifaceobj, 'down')
|
||||
return
|
||||
for op in ops:
|
||||
# first run ifupdownobj handlers. This is good enough
|
||||
# for the first object in the list
|
||||
handler = ifupdownobj.ops_handlers.get(op)
|
||||
if handler:
|
||||
try:
|
||||
handler(ifupdownobj, ifaceobjs[0])
|
||||
except Exception as e:
|
||||
if not ifupdownobj.link_master_slave_ignore_error(str(e)):
|
||||
ifupdownobj.logger.warn('%s: %s'
|
||||
%(ifaceobjs[0].name, str(e)))
|
||||
pass
|
||||
for ifaceobj in ifaceobjs:
|
||||
cls.run_iface_op(ifupdownobj, ifaceobj, op,
|
||||
cenv=ifupdownobj.generate_running_env(ifaceobj, op)
|
||||
if ifupdownobj.config.get('addon_scripts_support',
|
||||
'0') == '1' else None)
|
||||
posthookfunc = ifupdownobj.sched_hooks.get('posthook')
|
||||
if posthookfunc:
|
||||
try:
|
||||
[posthookfunc(ifupdownobj, ifaceobj, ops[0])
|
||||
for ifaceobj in ifaceobjs]
|
||||
except Exception as e:
|
||||
ifupdownobj.logger.warn('%s' %str(e))
|
||||
pass
|
||||
|
||||
@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.
|
||||
"""
|
||||
# proceed only for down operation
|
||||
if 'down' not in ops[0]:
|
||||
return True
|
||||
|
||||
if (ifupdownobj.FORCE or
|
||||
not ifupdownobj.ADDONS_ENABLE or
|
||||
(not ifupdownobj.is_ifaceobj_noconfig(ifaceobj) and
|
||||
ifupdownobj.config.get('warn_on_ifdown', '0') == '0' and
|
||||
not ifupdownobj.ALL)):
|
||||
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:
|
||||
if ifupdownobj.is_ifaceobj_noconfig(ifaceobj):
|
||||
ifupdownobj.logger.info('%s: skipping interface down,'
|
||||
%ifaceobj.name + ' upperiface %s still around ' %u)
|
||||
else:
|
||||
ifupdownobj.logger.warn('%s: skipping interface down,'
|
||||
%ifaceobj.name + ' upperiface %s still around ' %u)
|
||||
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)
|
||||
|
||||
# Check state of the dependent. If it is already brought up, return
|
||||
if (cls._STATE_CHECK and
|
||||
(ifaceobjs[0].state == ifaceState.from_str(ops[-1]))):
|
||||
ifupdownobj.logger.debug('%s: already processed' %ifacename)
|
||||
return
|
||||
|
||||
for ifaceobj in ifaceobjs:
|
||||
if not cls._check_upperifaces(ifupdownobj, ifaceobj,
|
||||
ops, parent, followdependents):
|
||||
return
|
||||
|
||||
# If inorder, run the iface first and then its dependents
|
||||
if order == ifaceSchedulerFlags.INORDER:
|
||||
cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
|
||||
|
||||
for ifaceobj in ifaceobjs:
|
||||
# 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 as 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_list_ops(ifupdownobj, ifaceobjs, 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 as 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)
|
||||
|
||||
if (cls._STATE_CHECK and
|
||||
(ifaceobjs[0].state == ifaceState.from_str(ops[-1]))):
|
||||
ifupdownobj.logger.debug('%s: already processed' %ifacename)
|
||||
return
|
||||
|
||||
if not skip_root:
|
||||
# run the iface first and then its upperifaces
|
||||
cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
|
||||
for ifaceobj in ifaceobjs:
|
||||
# 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 as 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 as e:
|
||||
if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
|
||||
traceback.print_tb(sys.exc_info()[2])
|
||||
ifupdownobj.logger.warn('%s : %s' %(ifacename, str(e)))
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def _get_valid_upperifaces(cls, ifupdownobj, ifacenames,
|
||||
allupperifacenames):
|
||||
""" Recursively find valid upperifaces
|
||||
|
||||
valid upperifaces are:
|
||||
- An upperiface which had no user config (example builtin
|
||||
interfaces. usually vlan interfaces.)
|
||||
- or had config and previously up
|
||||
- and interface currently does not exist
|
||||
- or is a bridge (because if your upperiface was a bridge
|
||||
- u will have to execute up on the bridge
|
||||
to enslave the port and apply bridge attributes to the port) """
|
||||
|
||||
upperifacenames = []
|
||||
for ifacename in ifacenames:
|
||||
# get upperifaces
|
||||
ifaceobj = ifupdownobj.get_ifaceobj_first(ifacename)
|
||||
if not ifaceobj:
|
||||
continue
|
||||
ulist = Set(ifaceobj.upperifaces).difference(upperifacenames)
|
||||
nulist = []
|
||||
for u in ulist:
|
||||
uifaceobj = ifupdownobj.get_ifaceobj_first(u)
|
||||
if not uifaceobj:
|
||||
continue
|
||||
has_config = not bool(uifaceobj.priv_flags
|
||||
& ifupdownobj.NOCONFIG)
|
||||
if (((has_config and ifupdownobj.get_ifaceobjs_saved(u)) or
|
||||
not has_config) and (not ifupdownobj.link_exists(u)
|
||||
or uifaceobj.link_kind == ifaceLinkKind.BRIDGE)):
|
||||
nulist.append(u)
|
||||
upperifacenames.extend(nulist)
|
||||
allupperifacenames.extend(upperifacenames)
|
||||
if upperifacenames:
|
||||
cls._get_valid_upperifaces(ifupdownobj, upperifacenames,
|
||||
allupperifacenames)
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def run_upperifaces(cls, ifupdownobj, ifacenames, ops,
|
||||
continueonfailure=True):
|
||||
""" Run through valid upperifaces """
|
||||
upperifaces = []
|
||||
|
||||
cls._get_valid_upperifaces(ifupdownobj, ifacenames, upperifaces)
|
||||
if not upperifaces:
|
||||
return
|
||||
# dump valid upperifaces
|
||||
ifupdownobj.logger.debug(upperifaces)
|
||||
for u in upperifaces:
|
||||
try:
|
||||
ifaceobjs = ifupdownobj.get_ifaceobjs(u)
|
||||
if not ifaceobjs:
|
||||
continue
|
||||
cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
|
||||
except Exception as e:
|
||||
if continueonfailure:
|
||||
self.logger.warn('%s' %str(e))
|
||||
|
||||
@classmethod
|
||||
def get_sorted_iface_list(cls, ifupdownobj, ifacenames, ops,
|
||||
dependency_graph, indegrees=None):
|
||||
if len(ifacenames) == 1:
|
||||
return ifacenames
|
||||
# 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)
|
||||
ifacenames_all_sorted = graph.topological_sort_graphs_all(
|
||||
dependency_graph, indegrees)
|
||||
# if ALL was set, return all interfaces
|
||||
if ifupdownobj.ALL:
|
||||
return ifacenames_all_sorted
|
||||
|
||||
# else return ifacenames passed as argument in sorted order
|
||||
ifacenames_sorted = []
|
||||
[ifacenames_sorted.append(ifacename)
|
||||
for ifacename in ifacenames_all_sorted
|
||||
if ifacename in ifacenames]
|
||||
return ifacenames_sorted
|
||||
|
||||
@classmethod
|
||||
def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
|
||||
dependency_graph=None, indegrees=None,
|
||||
order=ifaceSchedulerFlags.POSTORDER,
|
||||
followdependents=True, skipupperifaces=False, sort=False):
|
||||
""" runs interface configuration modules on interfaces passed as
|
||||
argument. Runs topological sort on interface dependency graph.
|
||||
|
||||
Args:
|
||||
**ifupdownobj** (object): ifupdownMain object
|
||||
|
||||
**ifacenames** (list): list of interface names
|
||||
|
||||
**ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
|
||||
|
||||
**dependency_graph** (dict): dependency graph in adjacency list format
|
||||
|
||||
Kwargs:
|
||||
**indegrees** (dict): indegree array of the dependency graph
|
||||
|
||||
**order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
|
||||
|
||||
**followdependents** (bool): follow dependent interfaces if true
|
||||
|
||||
**sort** (bool): sort ifacelist in the case where ALL is not set
|
||||
|
||||
"""
|
||||
#
|
||||
# Algo:
|
||||
# if ALL/auto interfaces are specified,
|
||||
# - walk the dependency tree in postorder or inorder depending
|
||||
# on the operation.
|
||||
# (This is to run interfaces correctly in order)
|
||||
# else:
|
||||
# - sort iface list if the ifaces belong to a "class"
|
||||
# - else just run iface list in the order they were specified
|
||||
#
|
||||
# Run any upperifaces if available
|
||||
#
|
||||
followupperifaces = False
|
||||
run_queue = []
|
||||
skip_ifacesort = int(ifupdownobj.config.get('skip_ifacesort', '0'))
|
||||
if not skip_ifacesort and not indegrees:
|
||||
indegrees = OrderedDict()
|
||||
for ifacename in dependency_graph.keys():
|
||||
indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
|
||||
|
||||
if not ifupdownobj.ALL:
|
||||
if 'up' in ops[0]:
|
||||
# If there is any interface that does not exist, maybe it
|
||||
# is a logical interface and we have to followupperifaces
|
||||
# when it comes up, so lets get that list.
|
||||
if any([True for i in ifacenames
|
||||
if ifupdownobj.must_follow_upperifaces(i)]):
|
||||
followupperifaces = (True if
|
||||
[i for i in ifacenames
|
||||
if not ifupdownobj.link_exists(i)]
|
||||
else False)
|
||||
# sort interfaces only if the caller asked to sort
|
||||
# and skip_ifacesort is not on.
|
||||
if not skip_ifacesort and sort:
|
||||
run_queue = cls.get_sorted_iface_list(ifupdownobj, ifacenames,
|
||||
ops, dependency_graph, indegrees)
|
||||
if run_queue and 'up' in ops[0]:
|
||||
run_queue.reverse()
|
||||
else:
|
||||
# if -a is set, we pick the interfaces
|
||||
# that have no parents and use a sorted list of those
|
||||
if not skip_ifacesort:
|
||||
sorted_ifacenames = cls.get_sorted_iface_list(ifupdownobj,
|
||||
ifacenames, ops, dependency_graph,
|
||||
indegrees)
|
||||
if sorted_ifacenames:
|
||||
# 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))
|
||||
else:
|
||||
ifupdownobj.logger.warn('interface sort returned None')
|
||||
|
||||
# If queue not present, just run interfaces that were asked by the
|
||||
# user
|
||||
if not run_queue:
|
||||
run_queue = list(ifacenames)
|
||||
# if we are taking the order of interfaces as specified
|
||||
# in the interfaces file, we should reverse the list if we
|
||||
# want to down. This can happen if 'skip_ifacesort'
|
||||
# is been specified.
|
||||
if 'down' in ops[0]:
|
||||
run_queue.reverse()
|
||||
|
||||
# run interface list
|
||||
cls.run_iface_list(ifupdownobj, run_queue, ops,
|
||||
parent=None, order=order,
|
||||
followdependents=followdependents)
|
||||
if not cls._SCHED_RETVAL:
|
||||
raise Exception()
|
||||
|
||||
if (not skipupperifaces and
|
||||
ifupdownobj.config.get('skip_upperifaces', '0') == '0' and
|
||||
((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 (parent interfaces) ' +
|
||||
'if available ..')
|
||||
cls._STATE_CHECK = False
|
||||
cls.run_upperifaces(ifupdownobj, ifacenames, ops)
|
||||
cls._STATE_CHECK = True
|
176
ifupdown/statemanager.py
Normal file
176
ifupdown/statemanager.py
Normal file
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
# stateManager --
|
||||
# interface state manager
|
||||
#
|
||||
import cPickle
|
||||
from collections import OrderedDict
|
||||
import logging
|
||||
import os
|
||||
from iface import *
|
||||
|
||||
class pickling():
|
||||
""" class with helper methods for pickling/unpickling iface objects """
|
||||
|
||||
@classmethod
|
||||
def save(cls, filename, list_of_objects):
|
||||
""" pickle a list of iface objects """
|
||||
try:
|
||||
with open(filename, 'w') as f:
|
||||
for obj in list_of_objects:
|
||||
cPickle.dump(obj, f, cPickle.HIGHEST_PROTOCOL)
|
||||
except:
|
||||
raise
|
||||
|
||||
@classmethod
|
||||
def save_obj(cls, f, obj):
|
||||
""" pickle iface object """
|
||||
try:
|
||||
cPickle.dump(obj, f, cPickle.HIGHEST_PROTOCOL)
|
||||
except:
|
||||
raise
|
||||
|
||||
@classmethod
|
||||
def load(cls, filename):
|
||||
""" load picked iface object """
|
||||
with open(filename, 'r') as f:
|
||||
while True:
|
||||
try: yield cPickle.load(f)
|
||||
except EOFError: break
|
||||
except: raise
|
||||
|
||||
class stateManager():
|
||||
""" state manager for managing ifupdown iface obj state
|
||||
|
||||
ifupdown2 has to maitain old objects for down operation on
|
||||
interfaces. ie to down or delete old configuration.
|
||||
|
||||
This class uses pickle to store iface objects.
|
||||
|
||||
"""
|
||||
|
||||
state_dir = '/run/network/'
|
||||
"""directory where the state file is stored """
|
||||
|
||||
state_filename = 'ifstatenew'
|
||||
"""name of the satefile """
|
||||
|
||||
def __init__(self):
|
||||
""" Initializes statemanager internal state
|
||||
|
||||
which includes a dictionary of last pickled iface objects
|
||||
"""
|
||||
self.ifaceobjdict = OrderedDict()
|
||||
self.logger = logging.getLogger('ifupdown.' +
|
||||
self.__class__.__name__)
|
||||
if not os.path.exists(self.state_dir):
|
||||
os.mkdir(self.state_dir)
|
||||
self.state_file = self.state_dir + self.state_filename
|
||||
|
||||
def save_ifaceobj(self, ifaceobj):
|
||||
self.ifaceobjdict.setdefault(ifaceobj.name,
|
||||
[]).append(ifaceobj)
|
||||
|
||||
def read_saved_state(self, filename=None):
|
||||
"""This member function reads saved iface objects
|
||||
|
||||
Kwargs:
|
||||
filename (str): name of the state file
|
||||
"""
|
||||
|
||||
pickle_filename = filename
|
||||
if not pickle_filename:
|
||||
pickle_filename = self.state_file
|
||||
if not os.path.exists(pickle_filename):
|
||||
return
|
||||
for ifaceobj in pickling.load(pickle_filename):
|
||||
self.save_ifaceobj(ifaceobj)
|
||||
|
||||
def get_ifaceobjs(self, ifacename):
|
||||
return self.ifaceobjdict.get(ifacename)
|
||||
|
||||
def ifaceobj_sync(self, ifaceobj, op):
|
||||
"""This member function sync's new obj state to old statemanager state
|
||||
|
||||
Args:
|
||||
ifaceobj (object): new iface object
|
||||
op (str): ifupdown operation
|
||||
"""
|
||||
|
||||
self.logger.debug('%s: statemanager sync state %s'
|
||||
%(ifaceobj.name, op))
|
||||
old_ifaceobjs = self.ifaceobjdict.get(ifaceobj.name)
|
||||
if 'up' in op:
|
||||
if not old_ifaceobjs:
|
||||
self.ifaceobjdict[ifaceobj.name] = [ifaceobj]
|
||||
else:
|
||||
# If it matches any of the object, return
|
||||
if any(o.compare(ifaceobj) for o in old_ifaceobjs):
|
||||
return
|
||||
# If it does not match any of the objects, and if
|
||||
# all objs in the list came from the pickled file,
|
||||
# then reset the list and add this object as a fresh one,
|
||||
# else append to the list
|
||||
if old_ifaceobjs[0].flags & iface._PICKLED:
|
||||
del self.ifaceobjdict[ifaceobj.name]
|
||||
self.ifaceobjdict[ifaceobj.name] = [ifaceobj]
|
||||
else:
|
||||
self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
|
||||
elif 'down' in op:
|
||||
# If down of object successfull, delete object from state manager
|
||||
if not old_ifaceobjs:
|
||||
return
|
||||
if ifaceobj.status != ifaceStatus.SUCCESS:
|
||||
return
|
||||
# If it matches any of the object, return
|
||||
oidx = 0
|
||||
for o in old_ifaceobjs:
|
||||
if o.compare(ifaceobj):
|
||||
old_ifaceobjs.pop(oidx)
|
||||
if not len(old_ifaceobjs):
|
||||
del self.ifaceobjdict[ifaceobj.name]
|
||||
return
|
||||
oidx += 1
|
||||
|
||||
def save_state(self):
|
||||
""" saves state (ifaceobjects) to persistent state file """
|
||||
|
||||
try:
|
||||
with open(self.state_file, 'w') as f:
|
||||
if not len(self.ifaceobjdict):
|
||||
f.truncate(0)
|
||||
return
|
||||
self.logger.debug('saving state ..')
|
||||
for ifaceobjs in self.ifaceobjdict.values():
|
||||
[pickling.save_obj(f, i) for i in ifaceobjs]
|
||||
except:
|
||||
raise
|
||||
|
||||
def dump_pretty(self, ifacenames, format='native'):
|
||||
if not ifacenames:
|
||||
ifacenames = self.ifaceobjdict.keys()
|
||||
for i in ifacenames:
|
||||
ifaceobjs = self.get_ifaceobjs(i)
|
||||
if not ifaceobjs:
|
||||
continue
|
||||
for ifaceobj in ifaceobjs:
|
||||
if format == 'json':
|
||||
ifaceobj.dump_json()
|
||||
else:
|
||||
ifaceobj.dump_pretty()
|
||||
|
||||
def dump(self, ifacenames=None):
|
||||
self.logger.debug('statemanager iface state:')
|
||||
if ifacenames:
|
||||
for i in ifacenames:
|
||||
ifaceobj = self.ifaces.get(i)
|
||||
if ifaceobj is None:
|
||||
raise ifaceNotFoundError('ifname %s'
|
||||
%i + ' not found')
|
||||
ifaceobj.dump(self.logger)
|
||||
else:
|
||||
for ifacename, ifaceobjs in self.ifaceobjdict.items():
|
||||
[i.dump(self.logger) for i in ifaceobjs]
|
59
ifupdown/template.py
Normal file
59
ifupdown/template.py
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
# template --
|
||||
# helper class to render templates
|
||||
#
|
||||
|
||||
import logging
|
||||
import traceback
|
||||
from utils import *
|
||||
|
||||
class templateEngine():
|
||||
""" provides template rendering methods """
|
||||
|
||||
def __init__(self, template_engine, template_lookuppath=None):
|
||||
self.logger = logging.getLogger('ifupdown.' +
|
||||
self.__class__.__name__)
|
||||
self.tclass = None
|
||||
self.tclassargs = {}
|
||||
self.render = self._render_default
|
||||
if template_engine == 'mako':
|
||||
try:
|
||||
self.tclass = utils.importName('mako.template', 'Template')
|
||||
except Exception as e:
|
||||
self.logger.warn('unable to load template engine %s (%s)'
|
||||
%(template_engine, str(e)))
|
||||
pass
|
||||
if template_lookuppath:
|
||||
try:
|
||||
self.logger.debug('setting template lookuppath to %s'
|
||||
%template_lookuppath)
|
||||
lc = utils.importName('mako.lookup', 'TemplateLookup')
|
||||
self.tclassargs['lookup'] = lc(
|
||||
directories=template_lookuppath.split(':'))
|
||||
except Exception as e:
|
||||
self.logger.warn('unable to set template lookup path' +
|
||||
' %s (%s)' %(template_lookuppath, str(e)))
|
||||
pass
|
||||
self.render = self._render_mako
|
||||
else:
|
||||
self.logger.info('skip template processing.., ' +
|
||||
'template engine not found')
|
||||
|
||||
def _render_default(self, textdata):
|
||||
return textdata
|
||||
|
||||
def _render_mako(self, textdata):
|
||||
""" render textdata passed as argument using mako
|
||||
|
||||
Returns rendered textdata """
|
||||
|
||||
if not self.tclass:
|
||||
return textdata
|
||||
self.logger.info('template processing on interfaces file ...')
|
||||
t = self.tclass(text=textdata, output_encoding='utf-8',
|
||||
lookup=self.tclassargs.get('lookup'))
|
||||
return t.render()
|
52
ifupdown/utils.py
Normal file
52
ifupdown/utils.py
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
# utils --
|
||||
# helper class
|
||||
#
|
||||
import os
|
||||
import fcntl
|
||||
import re
|
||||
|
||||
class utils():
|
||||
|
||||
@classmethod
|
||||
def importName(cls, modulename, name):
|
||||
""" Import a named object """
|
||||
try:
|
||||
module = __import__(modulename, globals(), locals(), [name])
|
||||
except ImportError:
|
||||
return None
|
||||
return getattr(module, name)
|
||||
|
||||
@classmethod
|
||||
def lockFile(cls, lockfile):
|
||||
try:
|
||||
fp = os.open(lockfile, os.O_CREAT | os.O_TRUNC | os.O_WRONLY)
|
||||
fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except IOError:
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def parse_iface_range(cls, name):
|
||||
range_match = re.match("^([\w\.]+)\[([\d]+)-([\d]+)\]", name)
|
||||
if range_match:
|
||||
range_groups = range_match.groups()
|
||||
if range_groups[1] and range_groups[2]:
|
||||
return (range_groups[0], int(range_groups[1], 10),
|
||||
int(range_groups[2], 10))
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def expand_iface_range(cls, name):
|
||||
ifacenames = []
|
||||
iface_range = cls.parse_iface_range(name)
|
||||
if iface_range:
|
||||
for i in range(iface_range[1], iface_range[2]):
|
||||
ifacenames.append('%s-%d' %(iface_range[0], i))
|
||||
return ifacenames
|
||||
|
||||
|
Reference in New Issue
Block a user