#!/usr/bin/python
+#
+# Copyright 2013. Cumulus Networks, Inc.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# iface --
+# interface object
+#
-import logging
+"""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)
-class ifaceFlags():
+ - ifaceJsonEncoder -- Json encoder for the iface object
- NONE = 0x1
- FOLLOW_DEPENDENTS = 0x2
+ - 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 """
- """iface status """
UNKNOWN = 0x1
SUCCESS = 0x2
ERROR = 0x3
+ NOTFOUND = 0x4
@classmethod
def to_str(cls, state):
return 'success'
elif state == cls.ERROR:
return 'error'
+ elif state == cls.NOTFOUND:
+ return 'notfound'
@classmethod
def from_str(cls, state_str):
return cls.ERROR
class ifaceState():
+ """Enumerates iface state """
- """ iface states """
UNKNOWN = 0x1
NEW = 0x2
PRE_UP = 0x3
DOWN = 0x7
POST_DOWN = 0x8
+ # Pseudo states
+ QUERY_CHECKCURR = 0x9
+ QUERY_RUNNING = 0xa
+
@classmethod
def to_str(cls, state):
if state == cls.UNKNOWN:
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):
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():
- """ config flags """
- AUTO = 0x1
- HOT_PLUG = 0x2
-
+ """ 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 = {}
- self.children = []
+ 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.dependents = None
+ self.lowerifaces = None
+ self.upperifaces = None
self.auto = False
self.classes = []
self.env = None
- self.config_current = {}
- self.raw_lines = []
+ self.raw_config = []
self.linkstate = None
def inc_refcnt(self):
- #print 'inside inc_refcnt = %d' %self.refcnt
self.refcnt += 1
def dec_refcnt(self):
self.refcnt -= 1
- def get_refcnt(self):
- return self.refcnt
-
- def set_refcnt(self, cnt):
- self.refcnt = 0
-
- def set_name(self, name):
- self.name = name
-
- def get_name(self):
- return self.name
-
- def set_addr_family(self, family):
- self.addr_family = family
-
- def get_addr_family(self):
- return self.addr_family
-
- def set_addr_method(self, method):
- self.addr_method = method
-
- def get_addr_method(self):
- return self.addr_method
-
- def set_config(self, config_dict):
- self.config = config_dict
-
- def get_config(self):
- return self.config
-
- def set_config_current(self, config_current):
- self.config_current = config_current
-
- def get_config_current(self):
- return self.config_current
-
- def get_auto(self):
- return self.auto
-
- def set_auto(self):
- self.auto = True
-
- def reset_auto(self):
- self.auto = False
-
- def get_classes(self):
- return self.classes
-
- def set_classes(self, classname):
- self.classes.append(classname)
-
- def belongs_to_class(self, intfclass):
- if intfclass in self.classes:
+ 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
- return False
-
- def add_child(self, child_iface_obj):
- self.children.append(child_iface_obj)
-
- def get_state(self):
- return self.state
-
- def get_state_str(self):
- return ifaceState.to_str(self.state)
+ def set_class(self, classname):
+ """ Appends a class to the list """
+ self.classes.append(classname)
- def set_state(self, state):
+ def set_state_n_status(self, state, status):
self.state = state
-
- def get_status(self):
- return self.status
-
- def get_status_str(self):
- return ifaceStatus.to_str(self.status)
-
- def set_status(self, status):
self.status = status
- def state_str_to_hex(self, state_str):
- return self.state_str_map.get(state_str)
-
def set_flag(self, flag):
self.flags |= flag
def clear_flag(self, flag):
self.flags &= ~flag
- def set_dependents(self, dlist):
- self.dependents = dlist
-
- def get_dependents(self):
- return self.dependents
-
- def set_linkstate(self, l):
- self.linkstate = l
-
- def get_linkstate(self):
- return self.linkstate
-
- def run_children(self, stage, module_list, force=0):
-
- if (self.is_flag_on(ifaceFlags.FOLLOW_DEPENDENTS)
- == False):
- return
-
- for c in self.children:
- self.set_flag(
- ifaceFlags.FOLLOW_DEPENDENTS)
- ret = c.run(stage, module_list, force)
- self.clear_flag(
- ifaceFlags.FOLLOW_DEPENDENTS)
-
- if ret != 0:
- self.set_state(stage, ret)
- if force == 0:
- break
-
- return ret
-
- def run(self, stage, module_list, force=0):
- ret = 0
- err = 0
-
- ret = self.run_children()
-
- if ret != 0 and force == 0:
- return ret
-
- for m in module_list:
- ret = m.run(self, stage, force)
- if ret != 0:
- self.set_state(stage, ret)
-
- # If no force, return!
- if force == 0:
- return -1
- err += 1
- else:
- self.set_state(stage, ret)
-
- return ret
+ 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):
- config = self.get_config()
-
- return config.get(attr_name)
+ return self.config.get(attr_name)
def get_attr_value_first(self, attr_name):
- config = self.get_config()
-
- attr_value_list = config.get(attr_name)
- if attr_value_list is not None:
+ 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):
- config = self.get_config()
-
- attr_value_list = config.get(attr_name)
- if attr_value_list is not None:
+ 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 self.env is None or len(self.env) == 0:
+ if not self.env:
self.generate_env()
return self.env
- def set_env(self, env):
- self.env = env
-
def generate_env(self):
env = {}
- config = self.get_config()
- env['IFACE'] = self.get_name()
+ 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)
- if len(env) > 0:
- self.set_env(env)
+ def update_config_dict(self, attrdict):
+ self.config.update(attrdict)
- def update_config(self, attr_name, attr_value, attr_status=None):
- if attr_value is None:
+ def update_config_with_status(self, attr_name, attr_value, attr_status=0):
+ if not attr_value:
attr_value = ''
- if attr_status == None:
- attr_status_str = ''
- elif attr_status == 0:
- attr_status_str = ' (success)'
- elif attr_status != 0:
- attr_status_str = ' (error)'
+ 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
- self.config[attr_name] = attr_value + attr_status_str
+ 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 is_different(self, dstiface):
+ if self.name != dstiface.name: return True
+ if self.addr_family != dstiface.addr_family: return True
+ if self.addr_method != dstiface.addr_method: return True
+ if self.auto != dstiface.auto: return True
+ if self.classes != dstiface.classes: return True
+ if any(True for k in self.config if k not in dstiface.config):
+ return True
+ if any(True for k,v in self.config.items()
+ if v != dstiface.config.get(k)): return True
+ return False
+
+ 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 = ' '
- print (self.raw_lines[0])
- for i in range(1, len(self.raw_lines)):
- print (indent + self.raw_lines[i])
+ print (self.raw_config[0])
+ for i in range(1, len(self.raw_config)):
+ print (indent + self.raw_config[i])
- def dump_state_pretty(self, logger):
- indent = ' '
- logger.info('iface %s' %self.get_name())
- for attr_name, attr_value in self.get_config_current().items():
- print (indent + '%s' %attr_name + ' %s' %attr_value)
-
def dump(self, logger):
indent = '\t'
- logger.info(self.get_name() + ' : {')
- logger.info(indent + 'family: %s' %self.get_addr_family())
- logger.info(indent + 'method: %s' %self.get_addr_method())
+ 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.get_state()))
+ %ifaceState.to_str(self.state))
logger.info(indent + 'status: %s'
- %ifaceStatus.to_str(self.get_status()))
- logger.info(indent + 'refcnt: %d' %self.get_refcnt())
- d = self.get_dependents()
- if d is not None:
- logger.info(indent + 'dependents: %s' %str(d))
+ %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 + 'dependents: None')
+ logger.info(indent + 'lowerdevs: None')
logger.info(indent + 'config: ')
- config = self.get_config()
- if config is not None:
+ config = self.config
+ if config:
logger.info(indent + indent + str(config))
logger.info('}')
- def dump_pretty(self, logger):
+ def dump_pretty(self, with_status=False):
indent = '\t'
- outbuf = 'iface %s' %self.get_name()
- if self.get_addr_family() is not None:
- outbuf += ' %s' %self.get_addr_family()
-
- if self.get_addr_method() is not None:
- outbuf += ' %s' %self.get_addr_method()
-
+ 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.get_config()
- if config is not None:
- for cname, cvalue in config.items():
- outbuf += indent + '%s' %cname + ' %s\n' %cvalue
-
- #outbuf += ('%s' %indent + '%s' %self.get_state_str() +
- # ' %s' %self.get_status_str())
-
+ 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)