Bugfixing LinuxNode and LinuxApplication
[nepi.git] / src / nepi / execution / resource.py
index a7e7758..078b07d 100644 (file)
@@ -1,9 +1,28 @@
-from neco.util.timefuncs import strfnow, strfdiff, strfvalid
-from neco.execution.trace import TraceAttr
+#
+#    NEPI, a framework to manage network experiments
+#    Copyright (C) 2013 INRIA
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.util.timefuncs import strfnow, strfdiff, strfvalid
+from nepi.util.logger import Logger
+from nepi.execution.trace import TraceAttr
 
 import copy
 import functools
-import inspect
 import logging
 import os
 import pkgutil
@@ -12,11 +31,17 @@ import weakref
 reschedule_delay = "0.5s"
 
 class ResourceAction:
+    """ Action that a user can order to a Resource Manager
+   
+    """
     DEPLOY = 0
     START = 1
     STOP = 2
 
 class ResourceState:
+    """ State of a Resource Manager
+   
+    """
     NEW = 0
     DISCOVERED = 1
     PROVISIONED = 2
@@ -27,33 +52,55 @@ class ResourceState:
     FAILED = 7
     RELEASED = 8
 
+ResourceState2str = dict({
+    ResourceState.NEW : "NEW",
+    ResourceState.DISCOVERED : "DISCOVERED",
+    ResourceState.PROVISIONED : "PROVISIONED",
+    ResourceState.READY : "READY",
+    ResourceState.STARTED : "STARTED",
+    ResourceState.STOPPED : "STOPPED",
+    ResourceState.FINISHED : "FINISHED",
+    ResourceState.FAILED : "FAILED",
+    ResourceState.RELEASED : "RELEASED",
+    })
+
 def clsinit(cls):
+    """ Initializes template information (i.e. attributes and traces)
+    for the ResourceManager class
+    """
     cls._clsinit()
     return cls
 
+def clsinit_copy(cls):
+    """ Initializes template information (i.e. attributes and traces)
+    for the ResourceManager class, inheriting attributes and traces
+    from the parent class
+    """
+    cls._clsinit_copy()
+    return cls
+
 # Decorator to invoke class initialization method
 @clsinit
-class ResourceManager(object):
+class ResourceManager(Logger):
     _rtype = "Resource"
-    _filters = None
     _attributes = None
     _traces = None
 
     @classmethod
-    def _register_filter(cls, attr):
+    def _register_attribute(cls, attr):
         """ Resource subclasses will invoke this method to add a 
-        filter attribute
+        resource attribute
 
         """
-        cls._filters[attr.name] = attr
+        cls._attributes[attr.name] = attr
 
     @classmethod
-    def _register_attribute(cls, attr):
-        """ Resource subclasses will invoke this method to add a 
+    def _remove_attribute(cls, name):
+        """ Resource subclasses will invoke this method to remove a 
         resource attribute
 
         """
-        cls._attributes[attr.name] = attr
+        del cls._attributes[name]
 
     @classmethod
     def _register_trace(cls, trace):
@@ -63,14 +110,13 @@ class ResourceManager(object):
         """
         cls._traces[trace.name] = trace
 
-
     @classmethod
-    def _register_filters(cls):
-        """ Resource subclasses will invoke this method to register 
-        resource filters
+    def _remove_trace(cls, name):
+        """ Resource subclasses will invoke this method to remove a 
+        resource trace
 
         """
-        pass
+        del cls._traces[name]
 
     @classmethod
     def _register_attributes(cls):
@@ -90,16 +136,14 @@ class ResourceManager(object):
 
     @classmethod
     def _clsinit(cls):
-        """ Create a new dictionnary instance of the dictionnary 
-        with the same template.
-        Each ressource should have the same registration dictionary
-        template with different instances.
+        """ ResourceManager child classes have different attributes and traces.
+        Since the templates that hold the information of attributes and traces
+        are 'class attribute' dictionaries, initially they all point to the 
+        parent class ResourceManager instances of those dictionaries. 
+        In order to make these templates independent from the parent's one,
+        it is necessary re-initialize the corresponding dictionaries. 
+        This is the objective of the _clsinit method
         """
-        # static template for resource filters
-        cls._filters = dict()
-        cls._register_filters()
-
         # static template for resource attributes
         cls._attributes = dict()
         cls._register_attributes()
@@ -109,15 +153,24 @@ class ResourceManager(object):
         cls._register_traces()
 
     @classmethod
-    def rtype(cls):
-        return cls._rtype
+    def _clsinit_copy(cls):
+        """ Same as _clsinit, except that it also inherits all attributes and traces
+        from the parent class.
+        """
+        # static template for resource attributes
+        cls._attributes = copy.deepcopy(cls._attributes)
+        cls._register_attributes()
+
+        # static template for resource traces
+        cls._traces = copy.deepcopy(cls._traces)
+        cls._register_traces()
 
     @classmethod
-    def get_filters(cls):
-        """ Returns a copy of the filters
+    def rtype(cls):
+        """ Returns the type of the Resource Manager
 
         """
-        return copy.deepcopy(cls._filters.values())
+        return cls._rtype
 
     @classmethod
     def get_attributes(cls):
@@ -134,6 +187,8 @@ class ResourceManager(object):
         return copy.deepcopy(cls._traces.values())
 
     def __init__(self, ec, guid):
+        super(ResourceManager, self).__init__(self.rtype())
+        
         self._guid = guid
         self._ec = weakref.ref(ec)
         self._connections = set()
@@ -154,53 +209,26 @@ class ResourceManager(object):
         self._ready_time = None
         self._release_time = None
 
-        # Logging
-        self._logger = logging.getLogger("Resource")
-
-    def debug(self, msg, out = None, err = None):
-        self.log(msg, logging.DEBUG, out, err)
-
-    def error(self, msg, out = None, err = None):
-        self.log(msg, logging.ERROR, out, err)
-
-    def warn(self, msg, out = None, err = None):
-        self.log(msg, logging.WARNING, out, err)
-
-    def info(self, msg, out = None, err = None):
-        self.log(msg, logging.INFO, out, err)
-
-    def log(self, msg, level, out = None, err = None):
-        if out:
-            msg += " - OUT: %s " % out
-
-        if err:
-            msg += " - ERROR: %s " % err
-
-        msg = self.log_message(msg)
-
-        self.logger.log(level, msg)
-
-    def log_message(self, msg):
-        return " %s guid: %d - %s " % (self._rtype, self.guid, msg)
-
-    @property
-    def logger(self):
-        return self._logger
-
     @property
     def guid(self):
+        """ Returns the guid of the current RM """
         return self._guid
 
     @property
     def ec(self):
+        """ Returns the Experiment Controller """
         return self._ec()
 
     @property
     def connections(self):
+        """ Returns the set of connection for this RM"""
         return self._connections
 
     @property
     def conditions(self):
+        """ Returns the list of conditions for this RM
+        The list is a dictionary with for each action, a list of tuple 
+        describing the conditions. """
         return self._conditions
 
     @property
@@ -235,22 +263,45 @@ class ResourceManager(object):
 
     @property
     def state(self):
+        """ Get the state of the current RM """
         return self._state
 
+    def log_message(self, msg):
+        """ Improve debugging message by adding more information 
+            as the guid and the type of the RM
+
+        :param msg: Message to log
+        :type msg: str
+        :rtype: str
+        """
+        return " %s guid: %d - %s " % (self._rtype, self.guid, msg)
+
     def connect(self, guid):
+        """ Connect the current RM with the RM 'guid'
+
+        :param guid: Guid of the RM the current RM will be connected
+        :type guid: int
+        """
         if self.valid_connection(guid):
             self._connections.add(guid)
 
-    def discover(self, filters = None):
+    def discover(self):
+        """ Discover the Resource. As it is specific for each RM, 
+        this method take the time when the RM become DISCOVERED and
+        change the status """
         self._discover_time = strfnow()
         self._state = ResourceState.DISCOVERED
 
-    def provision(self, filters = None):
+    def provision(self):
+        """ Provision the Resource. As it is specific for each RM, 
+        this method take the time when the RM become PROVISIONNED and
+        change the status """
         self._provision_time = strfnow()
         self._state = ResourceState.PROVISIONED
 
     def start(self):
-        """ Start the Resource Manager
+        """ Start the Resource Manager. As it is specific to each RM, this methods
+        just change, after some verifications, the status to STARTED and save the time.
 
         """
         if not self._state in [ResourceState.READY, ResourceState.STOPPED]:
@@ -261,7 +312,8 @@ class ResourceManager(object):
         self._state = ResourceState.STARTED
 
     def stop(self):
-        """ Start the Resource Manager
+        """ Stop the Resource Manager. As it is specific to each RM, this methods
+        just change, after some verifications, the status to STOPPED and save the time.
 
         """
         if not self._state in [ResourceState.STARTED]:
@@ -353,6 +405,12 @@ class ResourceManager(object):
         conditions.append((group, state, time))
 
     def get_connected(self, rtype):
+        """ Return the list of RM with the type 'rtype' 
+
+        :param rtype: Type of the RM we look for
+        :type rtype: str
+        :return : list of guid
+        """
         connected = []
         for guid in self.connections:
             rm = self.ec.get_resource(guid)
@@ -515,7 +573,7 @@ class ResourceManager(object):
             callback = functools.partial(self.stop_with_conditions)
             self.ec.schedule(delay, callback)
         else:
-            self.logger.debug(" ----- STOPPING ---- ") 
+            self.debug(" ----- STOPPING ---- ") 
             self.stop()
 
     def deploy(self):
@@ -526,7 +584,7 @@ class ResourceManager(object):
             self.error("Wrong state %s for deploy" % self.state)
             return
 
-        self.debug("----- DEPLOYING ---- ")
+        self.debug("----- READY ---- ")
         self._ready_time = strfnow()
         self._state = ResourceState.READY
 
@@ -538,7 +596,8 @@ class ResourceManager(object):
         self._state = ResourceState.RELEASED
 
     def valid_connection(self, guid):
-        """Check if the connection is available.
+        """Check if the connection is available. This method need to be 
+        redefined by each new Resource Manager.
 
         :param guid: Guid of the current Resource Manager
         :type guid: int
@@ -553,27 +612,37 @@ class ResourceFactory(object):
 
     @classmethod
     def resource_types(cls):
+        """Return the type of the Class"""
         return cls._resource_types
 
     @classmethod
     def register_type(cls, rclass):
+        """Register a new Ressource Manager"""
         cls._resource_types[rclass.rtype()] = rclass
 
     @classmethod
     def create(cls, rtype, ec, guid):
+        """Create a new instance of a Ressource Manager"""
         rclass = cls._resource_types[rtype]
         return rclass(ec, guid)
 
 def populate_factory():
+    """Register all the possible RM that exists in the current version of Nepi.
+    """
     for rclass in find_types():
         ResourceFactory.register_type(rclass)
 
 def find_types():
+    """Look into the different folders to find all the 
+    availables Resources Managers
+
+    """
     search_path = os.environ.get("NEPI_SEARCH_PATH", "")
     search_path = set(search_path.split(" "))
    
-    import neco.resources 
-    path = os.path.dirname(neco.resources.__file__)
+    import inspect
+    import nepi.resources 
+    path = os.path.dirname(nepi.resources.__file__)
     search_path.add(path)
 
     types = []
@@ -598,6 +667,7 @@ def find_types():
                     types.append(attr)
         except:
             import traceback
+            import logging
             err = traceback.format_exc()
             logger = logging.getLogger("Resource.find_types()")
             logger.error("Error while lading Resource Managers %s" % err)