1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00
Roopa Prabhu c9bba7539d Move LINK_MASTER/LINK_SLAVE to a new field link_type (purna hit some
issues with having it in flags) + add check for bond slave being already up

Ticket: CM-4408
Reviewed By:
Testing Done: some bond sanity test
2014-12-08 22:59:41 -08:00

520 lines
18 KiB
Python

#!/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 ifaceLinkType():
UNKNOWN = 0x0
LINK_SLAVE = 0x1
LINK_MASTER = 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
**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 = 0x1
HAS_SIBLINGS = 0x2
IFACERANGE_ENTRY = 0x3
IFACERANGE_START = 0x4
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 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.realname = None
self.link_type = ifaceLinkType.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, 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 self.get_attr_value_first(attr_name):
return
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 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['raw_config']
del odict['linkstate']
del odict['env']
del odict['link_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.priv_flags = 0
self.raw_config = []
self.flags |= self._PICKLED
self.link_type = ifaceLinkType.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