]> git.proxmox.com Git - mirror_ifupdown2.git/blobdiff - pkg/iface.py
cleanup (mostly cosmetic)
[mirror_ifupdown2.git] / pkg / iface.py
index d137b6acdcf596fc535115c8140718eb650c37c5..fca097d9a47f05eb7d821b84a37753682863d5ad 100644 (file)
@@ -7,18 +7,34 @@
 #    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 logging
 import json
+import logging
 
-class ifaceFlags():
-
-    NONE = 0x1
-    FOLLOW_DEPENDENTS = 0x2
+_tickmark = ' (' + u'\u2713'.encode('utf8') + ')'
+_crossmark = ' (' + u'\u2717'.encode('utf8') + ')'
 
 class ifaceStatus():
+    """Enumerates iface status """
 
-    """iface status """
     UNKNOWN = 0x1
     SUCCESS = 0x2
     ERROR = 0x3
@@ -33,7 +49,7 @@ class ifaceStatus():
         elif state == cls.ERROR:
             return 'error'
         elif state == cls.NOTFOUND:
-            return 'not found'
+            return 'notfound'
     
     @classmethod
     def from_str(cls, state_str):
@@ -45,8 +61,8 @@ class ifaceStatus():
             return cls.ERROR
 
 class ifaceState():
+    """Enumerates iface state """
 
-    """ iface states """
     UNKNOWN = 0x1
     NEW = 0x2
     PRE_UP = 0x3
@@ -56,6 +72,10 @@ class ifaceState():
     DOWN = 0x7
     POST_DOWN = 0x8
 
+    # Pseudo states
+    QUERY_CHECKCURR = 0x9
+    QUERY_RUNNING = 0xa
+
     @classmethod
     def to_str(cls, state):
         if state == cls.UNKNOWN:
@@ -70,8 +90,14 @@ class ifaceState():
             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):
@@ -87,37 +113,80 @@ class ifaceState():
             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 = OrderedDict()
-        self.children = []
+        self._config_status = {}
         self.state = ifaceState.NEW
         self.status = ifaceStatus.UNKNOWN
         self.flags = 0x0
+        self.priv_flags = 0x0
         self.refcnt = 0
-        # dependents that are listed as in the
-        # config file
-        self.dependents = None
-        # All dependents (includes dependents that
-        # are not listed in the config file)
-        self.realdev_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):
@@ -126,255 +195,196 @@ class iface():
     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 is_config_present(self):
-        if self.config is None:
+        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
-
-        return (len(self.config) != 0)
-
-    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, classes):
-        """ sets interface class list to the one passed as arg """
-        self.classes = classes
+        else:
+            return True
 
     def set_class(self, classname):
         """ Appends a class to the list """
         self.classes.append(classname)
 
-    def belongs_to_class(self, intfclass):
-        if intfclass in self.classes:
-            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_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_realdev_dependents(self, dlist):
-        self.realdev_dependents = dlist
-
-    def get_realdev_dependents(self):
-        return self.realdev_dependents
-
-    def set_linkstate(self, l):
-        self.linkstate = l
-
-    def get_linkstate(self):
-        return self.linkstate
+    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 len(env) > 0:
-            self.set_env(env)
+        if env:
+            self.env = env
 
     def update_config(self, attr_name, attr_value):
-        if self.config.get(attr_name) is None:
-            self.config[attr_name] = [attr_value]
-        else:
-            self.config[attr_name].append(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 attr_value is None:
+        if not attr_value:
             attr_value = ''
 
-        if attr_status != 0:
-            self.set_status(ifaceStatus.ERROR)
-        else:
-            if self.get_status() != ifaceStatus.ERROR:
-                self.set_status(ifaceStatus.SUCCESS)
-        if self.config.get(attr_name) is not None:
-            self.config[attr_name].append(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:
-            self.config[attr_name] = [attr_value]
-
-        """ XXX: If status needs to be encoded in the query string
-        if attr_status == 0:
-            self.set_status(attr
-            attr_status_str = ''
-        elif attr_status == 0:
-            attr_status_str = ' (success)'
-        elif attr_status != 0:
-            attr_status_str = ' (error)'
-        self.config[attr_name] = attr_value + attr_status_str """
+            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(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 + 'realdev dependents: %s'
-                    %str(self.get_realdev_dependents()))
+            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 = ''
-        if self.get_auto():
-            outbuf += 'auto %s\n' %self.get_name()
-        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()
-
+        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:
+        config = self.config
+        if config:
             for cname, cvaluelist in config.items():
+                idx = 0
                 for cv in cvaluelist:
-                    outbuf += indent + '%s' %cname + ' %s\n' %cv
-
-        #outbuf += ('%s' %indent + '%s' %self.get_state_str() +
-        #                ' %s' %self.get_status_str())
-
+                    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, logger):
-        json.dumps(self)
+    def dump_json(self, with_status=False):
+        print json.dumps(self, cls=ifaceJsonEncoder, indent=4)