mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
a logical interface comes up Ticket: CM-2493 Reviewed By: Testing Done: Tested ifup, ifdown in bond bridge setup Also, implicitly pick up the upperifaces (even when user has not specified --with-depends) for logical interfaces. This is because when a logical interface goes down/deleted, kernel impilicity deletes its upperifaces. so its better to implicitly bring up upperifaces. example bridge name bridge id STP enabled interfaces br0 8000.7072cf8c2fca yes bond1 bond2 br2000 8000.7072cf8c2fca yes bond1.2000 bond2.2000 br2001 8000.7072cf8c2fca yes bond1.2001 bond2.2001 bridge name bridge id STP enabled interfaces br0 8000.000000000000 yes br2000 8000.000000000000 yes br2001 8000.000000000000 yes bridge name bridge id STP enabled interfaces br0 8000.7072cf8c2fca yes bond1 bond2 br2000 8000.7072cf8c2fca yes bond1.2000 bond2.2000 br2001 8000.7072cf8c2fca yes bond1.2001 bond2.2001
397 lines
12 KiB
Python
397 lines
12 KiB
Python
#!/usr/bin/python
|
|
#
|
|
# Copyright 2013. Cumulus Networks, Inc.
|
|
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
|
#
|
|
# iface --
|
|
# interface object
|
|
#
|
|
|
|
"""ifupdown2 network interface object
|
|
|
|
It closely resembles the 'iface' object in /etc/network/interfaces
|
|
file. But can be extended to include any other network interface format
|
|
|
|
|
|
The module contains the following public classes:
|
|
|
|
- ifaceState -- enumerates iface object state
|
|
|
|
- ifaceStatus -- enumerates iface object status (success/error)
|
|
|
|
- ifaceJsonEncoder -- Json encoder for the iface object
|
|
|
|
- iface -- network in terface object class
|
|
|
|
"""
|
|
|
|
from collections import OrderedDict
|
|
import json
|
|
import logging
|
|
|
|
_tickmark = ' (' + u'\u2713'.encode('utf8') + ')'
|
|
_crossmark = ' (' + u'\u2717'.encode('utf8') + ')'
|
|
|
|
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:
|
|
for k, v in o.config.items():
|
|
if len(v) == 1:
|
|
retconfig[k] = v[0]
|
|
else:
|
|
retconfig[k] = v
|
|
|
|
return OrderedDict({'name' : o.name,
|
|
'addr_method' : o.addr_method,
|
|
'addr_family' : o.addr_family,
|
|
'auto' : o.auto,
|
|
'config' : retconfig})
|
|
|
|
class iface():
|
|
""" ifupdown2 interface 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
|
|
|
|
version = '0.1'
|
|
|
|
def __init__(self):
|
|
self.name = None
|
|
self.addr_family = None
|
|
self.addr_method = None
|
|
self.config = OrderedDict()
|
|
self._config_status = {}
|
|
self.state = ifaceState.NEW
|
|
self.status = ifaceStatus.UNKNOWN
|
|
self.flags = 0x0
|
|
self.priv_flags = 0x0
|
|
self.refcnt = 0
|
|
self.lowerifaces = None
|
|
self.upperifaces = None
|
|
self.auto = False
|
|
self.classes = []
|
|
self.env = None
|
|
self.raw_config = []
|
|
self.linkstate = None
|
|
|
|
def inc_refcnt(self):
|
|
self.refcnt += 1
|
|
|
|
def dec_refcnt(self):
|
|
self.refcnt -= 1
|
|
|
|
def is_config_present(self):
|
|
addr_method = self.addr_method
|
|
if addr_method:
|
|
if (addr_method.find('dhcp') != -1 or
|
|
addr_method.find('dhcp6') != -1):
|
|
return True
|
|
if not self.config:
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def set_class(self, classname):
|
|
""" Appends a class to the list """
|
|
self.classes.append(classname)
|
|
|
|
def set_state_n_status(self, state, status):
|
|
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):
|
|
if self.upperifaces:
|
|
if upperifacename not in self.upperifaces:
|
|
self.upperifaces.append(upperifacename)
|
|
else:
|
|
self.upperifaces = [upperifacename]
|
|
|
|
def get_attr_value(self, attr_name):
|
|
return self.config.get(attr_name)
|
|
|
|
def get_attr_value_first(self, attr_name):
|
|
attr_value_list = self.config.get(attr_name)
|
|
if attr_value_list:
|
|
return attr_value_list[0]
|
|
return None
|
|
|
|
def get_attr_value_n(self, attr_name, attr_index):
|
|
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):
|
|
if not self.env:
|
|
self.generate_env()
|
|
return self.env
|
|
|
|
def generate_env(self):
|
|
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):
|
|
self.config.setdefault(attr_name, []).append(attr_value)
|
|
|
|
def update_config_dict(self, attrdict):
|
|
self.config.update(attrdict)
|
|
|
|
def update_config_with_status(self, attr_name, attr_value, attr_status=0):
|
|
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:
|
|
self.status = ifaceStatus.ERROR
|
|
elif self.status != ifaceStatus.ERROR:
|
|
# Not already error, mark success
|
|
self.status = ifaceStatus.SUCCESS
|
|
|
|
def get_config_attr_status(self, attr_name, idx=0):
|
|
return self._config_status.get(attr_name, [])[idx]
|
|
|
|
def get_config_attr_status_str(self, attr_name, idx=0):
|
|
ret = self.get_config_attr_status(attr_name, idx)
|
|
if ret:
|
|
return _crossmark
|
|
else:
|
|
return _tickmark
|
|
|
|
def compare(self, dstiface):
|
|
""" Compares two objects
|
|
|
|
Returns True if object self is same as dstiface and False otherwise """
|
|
|
|
if self.name != dstiface.name: 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']
|
|
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
|
|
|
|
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):
|
|
indent = '\t'
|
|
outbuf = ''
|
|
if self.auto:
|
|
outbuf += 'auto %s\n' %self.name
|
|
outbuf += 'iface %s' %self.name
|
|
if self.addr_family:
|
|
outbuf += ' %s' %self.addr_family
|
|
if self.addr_method:
|
|
outbuf += ' %s' %self.addr_method
|
|
outbuf += '\n'
|
|
config = self.config
|
|
if config:
|
|
for cname, cvaluelist in config.items():
|
|
idx = 0
|
|
for cv in cvaluelist:
|
|
if with_status:
|
|
outbuf += indent + '%s %s %s\n' %(cname, cv,
|
|
self.get_config_attr_status_str(cname, idx))
|
|
else:
|
|
outbuf += indent + '%s %s\n' %(cname, cv)
|
|
idx += 1
|
|
print outbuf
|
|
|
|
def dump_json(self, with_status=False):
|
|
print json.dumps(self, cls=ifaceJsonEncoder, indent=4)
|