still making both branches closer
[nepi.git] / src / nepi / execution / resource.py
index be80329..4394130 100644 (file)
@@ -3,9 +3,8 @@
 #    Copyright (C) 2013 INRIA
 #
 #    This program is free software: you can redistribute it and/or modify
 #    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.
+#    it under the terms of the GNU General Public License version 2 as
+#    published by the Free Software Foundation;
 #
 #    This program is distributed in the hope that it will be useful,
 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
 #
 #    This program is distributed in the hope that it will be useful,
 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -31,8 +30,6 @@ import sys
 import threading
 import weakref
 
 import threading
 import weakref
 
-reschedule_delay = "1s"
-
 class ResourceAction:
     """ Action that a user can order to a Resource Manager
    
 class ResourceAction:
     """ Action that a user can order to a Resource Manager
    
@@ -47,16 +44,18 @@ class ResourceState:
     """
     NEW = 0
     DISCOVERED = 1
     """
     NEW = 0
     DISCOVERED = 1
-    PROVISIONED = 2
-    READY = 3
-    STARTED = 4
-    STOPPED = 5
-    FAILED = 6
-    RELEASED = 7
+    RESERVED = 2
+    PROVISIONED = 3
+    READY = 4
+    STARTED = 5
+    STOPPED = 6
+    FAILED = 7
+    RELEASED = 8
 
 ResourceState2str = dict({
     ResourceState.NEW : "NEW",
     ResourceState.DISCOVERED : "DISCOVERED",
 
 ResourceState2str = dict({
     ResourceState.NEW : "NEW",
     ResourceState.DISCOVERED : "DISCOVERED",
+    ResourceState.RESERVED : "RESERVED",
     ResourceState.PROVISIONED : "PROVISIONED",
     ResourceState.READY : "READY",
     ResourceState.STARTED : "STARTED",
     ResourceState.PROVISIONED : "PROVISIONED",
     ResourceState.READY : "READY",
     ResourceState.STARTED : "STARTED",
@@ -83,7 +82,7 @@ def clsinit(cls):
 
 def clsinit_copy(cls):
     """ Initializes template information (i.e. attributes and traces)
 
 def clsinit_copy(cls):
     """ Initializes template information (i.e. attributes and traces)
-    on classes direved from the ResourceManager class.
+    on classes derived from the ResourceManager class.
     It differs from the clsinit method in that it forces inheritance
     of attributes and traces from the parent class.
 
     It differs from the clsinit method in that it forces inheritance
     of attributes and traces from the parent class.
 
@@ -106,18 +105,20 @@ def clsinit_copy(cls):
 def failtrap(func):
     """ Decorator function for instance methods that should set the 
     RM state to FAILED when an error is raised. The methods that must be
 def failtrap(func):
     """ Decorator function for instance methods that should set the 
     RM state to FAILED when an error is raised. The methods that must be
-    decorated are: discover, provision, deploy, start, stop.
+    decorated are: discover, reserved, provision, deploy, start, stop.
 
     """
     def wrapped(self, *args, **kwargs):
         try:
             return func(self, *args, **kwargs)
         except:
 
     """
     def wrapped(self, *args, **kwargs):
         try:
             return func(self, *args, **kwargs)
         except:
+            self.fail()
+            
             import traceback
             err = traceback.format_exc()
             import traceback
             err = traceback.format_exc()
-            self.error(err)
-            self.debug("SETTING guid %d to state FAILED" % self.guid)
-            self.fail()
+            logger = Logger(self._rtype)
+            logger.error(err)
+            logger.error("SETTING guid %d to state FAILED" % self.guid)
             raise
     
     return wrapped
             raise
     
     return wrapped
@@ -127,19 +128,20 @@ class ResourceManager(Logger):
     """ Base clase for all ResourceManagers. 
     
     A ResourceManger is specific to a resource type (e.g. Node, 
     """ Base clase for all ResourceManagers. 
     
     A ResourceManger is specific to a resource type (e.g. Node, 
-    Switch, Application, etc) on a specific backend (e.g. PlanetLab, 
+    Switch, Application, etc) on a specific platform (e.g. PlanetLab, 
     OMF, etc).
 
     The ResourceManager instances are responsible for interacting with
     and controlling concrete (physical or virtual) resources in the 
     OMF, etc).
 
     The ResourceManager instances are responsible for interacting with
     and controlling concrete (physical or virtual) resources in the 
-    experimental backends.
+    experimental platforms.
     
     """
     _rtype = "Resource"
     _attributes = None
     _traces = None
     _help = None
     
     """
     _rtype = "Resource"
     _attributes = None
     _traces = None
     _help = None
-    _backend = None
+    _platform = None
+    _reschedule_delay = "0.5s"
 
     @classmethod
     def _register_attribute(cls, attr):
 
     @classmethod
     def _register_attribute(cls, attr):
@@ -186,23 +188,31 @@ class ResourceManager(Logger):
         attributes.
 
         """
         attributes.
 
         """
-        
         critical = Attribute("critical", 
                 "Defines whether the resource is critical. "
                 "A failure on a critical resource will interrupt "
                 "the experiment. ",
                 type = Types.Bool,
                 default = True,
         critical = Attribute("critical", 
                 "Defines whether the resource is critical. "
                 "A failure on a critical resource will interrupt "
                 "the experiment. ",
                 type = Types.Bool,
                 default = True,
-                flags = Flags.ExecReadOnly)
+                flags = Flags.Design)
+        hard_release = Attribute("hardRelease", 
+                "Forces removal of all result files and directories associated "
+                "to the RM upon resource release. After release the RM will "
+                "be removed from the EC and the results will not longer be "
+                "accessible",
+                type = Types.Bool,
+                default = False,
+                flags = Flags.Design)
 
         cls._register_attribute(critical)
 
         cls._register_attribute(critical)
+        cls._register_attribute(hard_release)
         
     @classmethod
     def _register_traces(cls):
         """ Resource subclasses will invoke this method to register
         resource traces
 
         
     @classmethod
     def _register_traces(cls):
         """ Resource subclasses will invoke this method to register
         resource traces
 
-        This method should be overriden in the RMs that define traces.
+        This method should be overridden in the RMs that define traces.
         
         """
         
         
         """
         
@@ -257,7 +267,7 @@ class ResourceManager(Logger):
         """ Returns a copy of the attributes
 
         """
         """ Returns a copy of the attributes
 
         """
-        return copy.deepcopy(cls._attributes.values())
+        return copy.deepcopy(list(cls._attributes.values()))
 
     @classmethod
     def get_attribute(cls, name):
 
     @classmethod
     def get_attribute(cls, name):
@@ -266,13 +276,12 @@ class ResourceManager(Logger):
         """
         return copy.deepcopy(cls._attributes[name])
 
         """
         return copy.deepcopy(cls._attributes[name])
 
-
     @classmethod
     def get_traces(cls):
         """ Returns a copy of the traces
 
         """
     @classmethod
     def get_traces(cls):
         """ Returns a copy of the traces
 
         """
-        return copy.deepcopy(cls._traces.values())
+        return copy.deepcopy(list(cls._traces.values()))
 
     @classmethod
     def get_help(cls):
 
     @classmethod
     def get_help(cls):
@@ -282,12 +291,38 @@ class ResourceManager(Logger):
         return cls._help
 
     @classmethod
         return cls._help
 
     @classmethod
-    def get_backend(cls):
-        """ Returns the identified of the backend (i.e. testbed, environment)
+    def get_platform(cls):
+        """ Returns the identified of the platform (i.e. testbed type)
         for the Resource
 
         """
         for the Resource
 
         """
-        return cls._backend
+        return cls._platform
+
+    @classmethod
+    def get_global(cls, name):
+        """ Returns the value of a global attribute
+            Global attribute meaning an attribute for 
+            all the resources from a rtype
+
+        :param name: Name of the attribute
+        :type name: str
+        :rtype: str
+        """
+        global_attr = cls._attributes[name]
+        return global_attr.value
+
+    @classmethod
+    def set_global(cls, name, value):
+        """ Set value for a global attribute
+
+        :param name: Name of the attribute
+        :type name: str
+        :param name: Value of the attribute
+        :type name: str
+        """
+        global_attr = cls._attributes[name]
+        global_attr.value = value
+        return value
 
     def __init__(self, ec, guid):
         super(ResourceManager, self).__init__(self.get_rtype())
 
     def __init__(self, ec, guid):
         super(ResourceManager, self).__init__(self.get_rtype())
@@ -310,6 +345,7 @@ class ResourceManager(Logger):
         self._start_time = None
         self._stop_time = None
         self._discover_time = None
         self._start_time = None
         self._stop_time = None
         self._discover_time = None
+        self._reserved_time = None
         self._provision_time = None
         self._ready_time = None
         self._release_time = None
         self._provision_time = None
         self._ready_time = None
         self._release_time = None
@@ -319,7 +355,7 @@ class ResourceManager(Logger):
 
         # instance lock to synchronize exclusive state change methods (such
         # as deploy and release methods), in order to prevent them from being 
 
         # instance lock to synchronize exclusive state change methods (such
         # as deploy and release methods), in order to prevent them from being 
-        # executed at the same time
+        # executed at the same time and corrupt internal resource state
         self._release_lock = threading.Lock()
 
     @property
         self._release_lock = threading.Lock()
 
     @property
@@ -362,6 +398,11 @@ class ResourceManager(Logger):
         """ Returns the discover time of the RM as a timestamp """
         return self._discover_time
 
         """ Returns the discover time of the RM as a timestamp """
         return self._discover_time
 
+    @property
+    def reserved_time(self):
+        """ Returns the reserved time of the RM as a timestamp """
+        return self._reserved_time
+
     @property
     def provision_time(self):
         """ Returns the provision time of the RM as a timestamp """
     @property
     def provision_time(self):
         """ Returns the provision time of the RM as a timestamp """
@@ -379,7 +420,7 @@ class ResourceManager(Logger):
 
     @property
     def failed_time(self):
 
     @property
     def failed_time(self):
-        """ Returns the time failure occured for the RM as a timestamp """
+        """ Returns the time failure occurred for the RM as a timestamp """
         return self._failed_time
 
     @property
         return self._failed_time
 
     @property
@@ -387,6 +428,11 @@ class ResourceManager(Logger):
         """ Get the current state of the RM """
         return self._state
 
         """ Get the current state of the RM """
         return self._state
 
+    @property
+    def reschedule_delay(self):
+        """ Returns default reschedule delay """
+        return self._reschedule_delay
+
     def log_message(self, msg):
         """ Returns the log message formatted with added information.
 
     def log_message(self, msg):
         """ Returns the log message formatted with added information.
 
@@ -395,12 +441,12 @@ class ResourceManager(Logger):
         :rtype: str
 
         """
         :rtype: str
 
         """
-        return " %s guid: %d - %s " % (self._rtype, self.guid, msg)
+        return " %s guid %d - %s " % (self._rtype, self.guid, msg)
 
     def register_connection(self, guid):
         """ Registers a connection to the RM identified by guid
 
 
     def register_connection(self, guid):
         """ Registers a connection to the RM identified by guid
 
-        This method should not be overriden. Specific functionality
+        This method should not be overridden. Specific functionality
         should be added in the do_connect method.
 
         :param guid: Global unique identified of the RM to connect to
         should be added in the do_connect method.
 
         :param guid: Global unique identified of the RM to connect to
@@ -414,7 +460,7 @@ class ResourceManager(Logger):
     def unregister_connection(self, guid):
         """ Removes a registered connection to the RM identified by guid
         
     def unregister_connection(self, guid):
         """ Removes a registered connection to the RM identified by guid
         
-        This method should not be overriden. Specific functionality
+        This method should not be overridden. Specific functionality
         should be added in the do_disconnect method.
 
         :param guid: Global unique identified of the RM to connect to
         should be added in the do_disconnect method.
 
         :param guid: Global unique identified of the RM to connect to
@@ -432,7 +478,7 @@ class ResourceManager(Logger):
         This  method is responsible for selecting an individual resource
         matching user requirements.
 
         This  method is responsible for selecting an individual resource
         matching user requirements.
 
-        This method should not be overriden directly. Specific functionality
+        This method should not be overridden directly. Specific functionality
         should be added in the do_discover method.
 
         """
         should be added in the do_discover method.
 
         """
@@ -440,6 +486,21 @@ class ResourceManager(Logger):
             if self._state != ResourceState.RELEASED:
                 self.do_discover()
 
             if self._state != ResourceState.RELEASED:
                 self.do_discover()
 
+    @failtrap
+    def reserve(self):
+        """ Performs resource reserve.
+        
+        This  method is responsible for reserving an individual resource
+        matching user requirements.
+
+        This method should not be overridden directly. Specific functionality
+        should be added in the do_reserved method.
+
+        """
+        with self._release_lock:
+            if self._state != ResourceState.RELEASED:
+                self.do_reserve()
+
     @failtrap
     def provision(self):
         """ Performs resource provisioning.
     @failtrap
     def provision(self):
         """ Performs resource provisioning.
@@ -448,7 +509,7 @@ class ResourceManager(Logger):
         After this method has been successfully invoked, the resource
         should be accessible/controllable by the RM.
 
         After this method has been successfully invoked, the resource
         should be accessible/controllable by the RM.
 
-        This method should not be overriden directly. Specific functionality
+        This method should not be overridden directly. Specific functionality
         should be added in the do_provision method.
 
         """
         should be added in the do_provision method.
 
         """
@@ -456,6 +517,22 @@ class ResourceManager(Logger):
             if self._state != ResourceState.RELEASED:
                 self.do_provision()
 
             if self._state != ResourceState.RELEASED:
                 self.do_provision()
 
+    @failtrap
+    def configure(self):
+        """ Performs resource configuration.
+
+        This  method is responsible for configuring one resource.
+        After this method has been successfully invoked, the resource
+        should be set up to start the experimentation.
+
+        This method should not be overridden directly. Specific functionality
+        should be added in the do_configure method.
+
+        """
+        with self._release_lock:
+            if self._state != ResourceState.RELEASED:
+                self.do_configure()
+
     @failtrap
     def start(self):
         """ Starts the RM (e.g. launch remote process).
     @failtrap
     def start(self):
         """ Starts the RM (e.g. launch remote process).
@@ -463,7 +540,7 @@ class ResourceManager(Logger):
         There is no standard start behavior. Some RMs will not need to perform
         any actions upon start.
 
         There is no standard start behavior. Some RMs will not need to perform
         any actions upon start.
 
-        This method should not be overriden directly. Specific functionality
+        This method should not be overridden directly. Specific functionality
         should be added in the do_start method.
 
         """
         should be added in the do_start method.
 
         """
@@ -483,7 +560,7 @@ class ResourceManager(Logger):
         There is no standard stop behavior. Some RMs will not need to perform
         any actions upon stop.
     
         There is no standard stop behavior. Some RMs will not need to perform
         any actions upon stop.
     
-        This method should not be overriden directly. Specific functionality
+        This method should not be overridden directly. Specific functionality
         should be added in the do_stop method.
       
         """
         should be added in the do_stop method.
       
         """
@@ -501,7 +578,7 @@ class ResourceManager(Logger):
         This method is responsible for deploying the resource (and invoking 
         the discover and provision methods).
  
         This method is responsible for deploying the resource (and invoking 
         the discover and provision methods).
  
-        This method should not be overriden directly. Specific functionality
+        This method should not be overridden directly. Specific functionality
         should be added in the do_deploy method.
        
         """
         should be added in the do_deploy method.
        
         """
@@ -512,7 +589,6 @@ class ResourceManager(Logger):
         with self._release_lock:
             if self._state != ResourceState.RELEASED:
                 self.do_deploy()
         with self._release_lock:
             if self._state != ResourceState.RELEASED:
                 self.do_deploy()
-                self.debug("----- READY ---- ")
 
     def release(self):
         """ Perform actions to free resources used by the RM.
 
     def release(self):
         """ Perform actions to free resources used by the RM.
@@ -520,7 +596,7 @@ class ResourceManager(Logger):
         This  method is responsible for releasing resources that were
         used during the experiment by the RM.
 
         This  method is responsible for releasing resources that were
         used during the experiment by the RM.
 
-        This method should not be overriden directly. Specific functionality
+        This method should not be overridden directly. Specific functionality
         should be added in the do_release method.
       
         """
         should be added in the do_release method.
       
         """
@@ -528,17 +604,19 @@ class ResourceManager(Logger):
             try:
                 self.do_release()
             except:
             try:
                 self.do_release()
             except:
+                self.set_released()
+
                 import traceback
                 err = traceback.format_exc()
                 import traceback
                 err = traceback.format_exc()
-                self.error(err)
-
-            self.set_released()
-            self.debug("----- RELEASED ---- ")
+                msg = " %s guid %d ----- FAILED TO RELEASE ----- \n %s " % (
+                        self._rtype, self.guid, err)
+                logger = Logger(self._rtype)
+                logger.debug(msg)
 
     def fail(self):
         """ Sets the RM to state FAILED.
 
 
     def fail(self):
         """ Sets the RM to state FAILED.
 
-        This method should not be overriden directly. Specific functionality
+        This method should not be overridden directly. Specific functionality
         should be added in the do_fail method.
 
         """
         should be added in the do_fail method.
 
         """
@@ -556,6 +634,7 @@ class ResourceManager(Logger):
         """
         attr = self._attrs[name]
         attr.value = value
         """
         attr = self._attrs[name]
         attr.value = value
+        return value
 
     def get(self, name):
         """ Returns the value of the attribute
 
     def get(self, name):
         """ Returns the value of the attribute
@@ -565,8 +644,43 @@ class ResourceManager(Logger):
         :rtype: str
         """
         attr = self._attrs[name]
         :rtype: str
         """
         attr = self._attrs[name]
+
+        """
+        A.Q. Commenting due to performance impact
+        if attr.has_flag(Flags.Global):
+            self.warning( "Attribute %s is global. Use get_global instead." % name)
+        """
+            
         return attr.value
 
         return attr.value
 
+    def has_changed(self, name):
+        """ Returns the True is the value of the attribute
+            has been modified by the user.
+
+        :param name: Name of the attribute
+        :type name: str
+        :rtype: str
+        """
+        attr = self._attrs[name]
+        return attr.has_changed
+
+    def has_flag(self, name, flag):
+        """ Returns true if the attribute has the flag 'flag'
+
+        :param flag: Flag to be checked
+        :type flag: Flags
+        """
+        attr = self._attrs[name]
+        return attr.has_flag(flag)
+
+    def has_attribute(self, name):
+        """ Returns true if the RM has an attribute with name
+
+        :param name: name of the attribute
+        :type name: string
+        """
+        return name in self._attrs
+
     def enable_trace(self, name):
         """ Explicitly enable trace generation
 
     def enable_trace(self, name):
         """ Explicitly enable trace generation
 
@@ -651,7 +765,7 @@ class ResourceManager(Logger):
         if not isinstance(group, list):
             group = [group]
 
         if not isinstance(group, list):
             group = [group]
 
-        for act, conditions in self.conditions.iteritems():
+        for act, conditions in self.conditions.items():
             if action and act != action:
                 continue
 
             if action and act != action:
                 continue
 
@@ -682,6 +796,18 @@ class ResourceManager(Logger):
                 connected.append(rm)
         return connected
 
                 connected.append(rm)
         return connected
 
+    def is_rm_instance(self, rtype):
+        """ Returns True if the RM is instance of 'rtype'
+
+        :param rtype: Type of the RM we look for
+        :type rtype: str
+        :return: True|False
+        """
+        rclass = ResourceFactory.get_resource_type(rtype)
+        if isinstance(self, rclass):
+            return True
+        return False
+
     @failtrap
     def _needs_reschedule(self, group, state, time):
         """ Internal method that verify if 'time' has elapsed since 
     @failtrap
     def _needs_reschedule(self, group, state, time):
         """ Internal method that verify if 'time' has elapsed since 
@@ -700,17 +826,19 @@ class ResourceManager(Logger):
 
         """
         reschedule = False
 
         """
         reschedule = False
-        delay = reschedule_delay 
+        delay = self.reschedule_delay 
 
         # check state and time elapsed on all RMs
         for guid in group:
             rm = self.ec.get_resource(guid)
             
             # If one of the RMs this resource needs to wait for has FAILED
 
         # check state and time elapsed on all RMs
         for guid in group:
             rm = self.ec.get_resource(guid)
             
             # If one of the RMs this resource needs to wait for has FAILED
-            # we raise an exception
+            # and is critical we raise an exception
             if rm.state == ResourceState.FAILED:
             if rm.state == ResourceState.FAILED:
+                if not rm.get('critical'):
+                    continue
                 msg = "Resource can not wait for FAILED RM %d. Setting Resource to FAILED"
                 msg = "Resource can not wait for FAILED RM %d. Setting Resource to FAILED"
-                raise RuntimeError, msg
+                raise RuntimeError(msg)
 
             # If the RM state is lower than the requested state we must
             # reschedule (e.g. if RM is READY but we required STARTED).
 
             # If the RM state is lower than the requested state we must
             # reschedule (e.g. if RM is READY but we required STARTED).
@@ -723,7 +851,9 @@ class ResourceManager(Logger):
             if time:
                 if state == ResourceState.DISCOVERED:
                     t = rm.discover_time
             if time:
                 if state == ResourceState.DISCOVERED:
                     t = rm.discover_time
-                if state == ResourceState.PROVISIONED:
+                elif state == ResourceState.RESERVED:
+                    t = rm.reserved_time
+                elif state == ResourceState.PROVISIONED:
                     t = rm.provision_time
                 elif state == ResourceState.READY:
                     t = rm.ready_time
                     t = rm.provision_time
                 elif state == ResourceState.READY:
                     t = rm.ready_time
@@ -767,7 +897,7 @@ class ResourceManager(Logger):
         """
 
         reschedule = False
         """
 
         reschedule = False
-        delay = reschedule_delay 
+        delay = self.reschedule_delay 
 
         ## evaluate if set conditions are met
 
 
         ## evaluate if set conditions are met
 
@@ -792,7 +922,7 @@ class ResourceManager(Logger):
         #import pdb;pdb.set_trace()
 
         reschedule = False
         #import pdb;pdb.set_trace()
 
         reschedule = False
-        delay = reschedule_delay 
+        delay = self.reschedule_delay 
 
 
         ## evaluate if conditions to start are met
 
 
         ## evaluate if conditions to start are met
@@ -811,12 +941,12 @@ class ResourceManager(Logger):
             # Verify all start conditions are met
             for (group, state, time) in start_conditions:
                 # Uncomment for debug
             # Verify all start conditions are met
             for (group, state, time) in start_conditions:
                 # Uncomment for debug
-                unmet = []
-                for guid in group:
-                    rm = self.ec.get_resource(guid)
-                    unmet.append((guid, rm._state))
-                
-                self.debug("---- WAITED STATES ---- %s" % unmet )
+                #unmet = []
+                #for guid in group:
+                #    rm = self.ec.get_resource(guid)
+                #    unmet.append((guid, rm._state))
+                #
+                #self.debug("---- WAITED STATES ---- %s" % unmet )
 
                 reschedule, delay = self._needs_reschedule(group, state, time)
                 if reschedule:
 
                 reschedule, delay = self._needs_reschedule(group, state, time)
                 if reschedule:
@@ -834,7 +964,7 @@ class ResourceManager(Logger):
 
         """
         reschedule = False
 
         """
         reschedule = False
-        delay = reschedule_delay 
+        delay = self.reschedule_delay 
 
         ## evaluate if conditions to stop are met
         if self.ec.abort:
 
         ## evaluate if conditions to stop are met
         if self.ec.abort:
@@ -867,15 +997,16 @@ class ResourceManager(Logger):
 
         """
         reschedule = False
 
         """
         reschedule = False
-        delay = reschedule_delay 
+        delay = self.reschedule_delay 
 
         ## evaluate if conditions to deploy are met
         if self.ec.abort:
             return 
 
         # only can deploy when RM is either NEW, DISCOVERED or PROVISIONED 
 
         ## evaluate if conditions to deploy are met
         if self.ec.abort:
             return 
 
         # only can deploy when RM is either NEW, DISCOVERED or PROVISIONED 
-        if self.state not in [ResourceState.NEW, ResourceState.DISCOVERED, 
-                ResourceState.PROVISIONED]:
+        if self.state not in [ResourceState.NEW, ResourceState.DISCOVERED,
+                ResourceState.RESERVED, ResourceState.PROVISIONED]:
+            #### XXX: A.Q. IT SHOULD FAIL IF DEPLOY IS CALLED IN OTHER STATES!
             reschedule = True
             self.debug("---- RESCHEDULING DEPLOY ---- state %s " % self.state )
         else:
             reschedule = True
             self.debug("---- RESCHEDULING DEPLOY ---- state %s " % self.state )
         else:
@@ -890,7 +1021,7 @@ class ResourceManager(Logger):
                 #for guid in group:
                 #    rm = self.ec.get_resource(guid)
                 #    unmet.append((guid, rm._state))
                 #for guid in group:
                 #    rm = self.ec.get_resource(guid)
                 #    unmet.append((guid, rm._state))
-                #
+                
                 #self.debug("---- WAITED STATES ---- %s" % unmet )
 
                 reschedule, delay = self._needs_reschedule(group, state, time)
                 #self.debug("---- WAITED STATES ---- %s" % unmet )
 
                 reschedule, delay = self._needs_reschedule(group, state, time)
@@ -931,9 +1062,15 @@ class ResourceManager(Logger):
     def do_discover(self):
         self.set_discovered()
 
     def do_discover(self):
         self.set_discovered()
 
+    def do_reserve(self):
+        self.set_reserved()
+
     def do_provision(self):
         self.set_provisioned()
 
     def do_provision(self):
         self.set_provisioned()
 
+    def do_configure(self):
+        pass
+
     def do_start(self):
         self.set_started()
 
     def do_start(self):
         self.set_started()
 
@@ -944,45 +1081,71 @@ class ResourceManager(Logger):
         self.set_ready()
 
     def do_release(self):
         self.set_ready()
 
     def do_release(self):
-        pass
+        self.set_released()
 
     def do_fail(self):
         self.set_failed()
 
     def do_fail(self):
         self.set_failed()
+        self.ec.inform_failure(self.guid)
 
 
-    def set_started(self):
+    def set_started(self, time = None):
         """ Mark ResourceManager as STARTED """
         """ Mark ResourceManager as STARTED """
-        self.set_state(ResourceState.STARTED, "_start_time")
-        
-    def set_stopped(self):
+        self.set_state(ResourceState.STARTED, "_start_time", time)
+        self.debug("----- STARTED ---- ")
+
+    def set_stopped(self, time = None):
         """ Mark ResourceManager as STOPPED """
         """ Mark ResourceManager as STOPPED """
-        self.set_state(ResourceState.STOPPED, "_stop_time")
+        self.set_state(ResourceState.STOPPED, "_stop_time", time)
+        self.debug("----- STOPPED ---- ")
 
 
-    def set_ready(self):
+    def set_ready(self, time = None):
         """ Mark ResourceManager as READY """
         """ Mark ResourceManager as READY """
-        self.set_state(ResourceState.READY, "_ready_time")
+        self.set_state(ResourceState.READY, "_ready_time", time)
+        self.debug("----- READY ---- ")
 
 
-    def set_released(self):
+    def set_released(self, time = None):
         """ Mark ResourceManager as REALEASED """
         """ Mark ResourceManager as REALEASED """
-        self.set_state(ResourceState.RELEASED, "_release_time")
+        self.set_state(ResourceState.RELEASED, "_release_time", time)
 
 
-    def set_failed(self):
+        msg = " %s guid %d ----- RELEASED ----- " % (self._rtype, self.guid)
+        logger = Logger(self._rtype)
+        logger.debug(msg)
+
+    def set_failed(self, time = None):
         """ Mark ResourceManager as FAILED """
         """ Mark ResourceManager as FAILED """
-        self.set_state(ResourceState.FAILED, "_failed_time")
+        self.set_state(ResourceState.FAILED, "_failed_time", time)
+
+        msg = " %s guid %d ----- FAILED ----- " % (self._rtype, self.guid)
+        logger = Logger(self._rtype)
+        logger.debug(msg)
 
 
-    def set_discovered(self):
+    def set_discovered(self, time = None):
         """ Mark ResourceManager as DISCOVERED """
         """ Mark ResourceManager as DISCOVERED """
-        self.set_state(ResourceState.DISCOVERED, "_discover_time")
+        self.set_state(ResourceState.DISCOVERED, "_discover_time", time)
+        self.debug("----- DISCOVERED ---- ")
+
+    def set_reserved(self, time = None):
+        """ Mark ResourceManager as RESERVED """
+        self.set_state(ResourceState.RESERVED, "_reserved_time", time)
+        self.debug("----- RESERVED ---- ")
 
 
-    def set_provisioned(self):
+    def set_provisioned(self, time = None):
         """ Mark ResourceManager as PROVISIONED """
         """ Mark ResourceManager as PROVISIONED """
-        self.set_state(ResourceState.PROVISIONED, "_provision_time")
+        self.set_state(ResourceState.PROVISIONED, "_provision_time", time)
+        self.debug("----- PROVISIONED ---- ")
+
+    def set_state(self, state, state_time_attr, time = None):
+        """ Set the state of the RM while keeping a trace of the time """
 
 
-    def set_state(self, state, state_time_attr):
         # Ensure that RM state will not change after released
         if self._state == ResourceState.RELEASED:
             return 
         # Ensure that RM state will not change after released
         if self._state == ResourceState.RELEASED:
             return 
-   
-        setattr(self, state_time_attr, tnow())
+
+        time = time or tnow()
+        self.set_state_time(state, state_time_attr, time)
+  
+    def set_state_time(self, state, state_time_attr, time):
+        """ Set the time for the RM state change """
+        setattr(self, state_time_attr, time)
         self._state = state
 
 class ResourceFactory(object):
         self._state = state
 
 class ResourceFactory(object):
@@ -1010,7 +1173,7 @@ class ResourceFactory(object):
         return rclass(ec, guid)
 
 def populate_factory():
         return rclass(ec, guid)
 
 def populate_factory():
-    """Register all the possible RM that exists in the current version of Nepi.
+    """Find and rgister all available RMs
     """
     # Once the factory is populated, don't repopulate
     if not ResourceFactory.resource_types():
     """
     # Once the factory is populated, don't repopulate
     if not ResourceFactory.resource_types():
@@ -1029,7 +1192,7 @@ def find_types():
     path = os.path.dirname(nepi.resources.__file__)
     search_path.add(path)
 
     path = os.path.dirname(nepi.resources.__file__)
     search_path.add(path)
 
-    types = []
+    types = set()
 
     for importer, modname, ispkg in pkgutil.walk_packages(search_path, 
             prefix = "nepi.resources."):
 
     for importer, modname, ispkg in pkgutil.walk_packages(search_path, 
             prefix = "nepi.resources."):
@@ -1037,7 +1200,7 @@ def find_types():
         loader = importer.find_module(modname)
         
         try:
         loader = importer.find_module(modname)
         
         try:
-            # Notice: Repeated calls to load_module will act as a reload of teh module
+            # Notice: Repeated calls to load_module will act as a reload of the module
             if modname in sys.modules:
                 module = sys.modules.get(modname)
             else:
             if modname in sys.modules:
                 module = sys.modules.get(modname)
             else:
@@ -1056,7 +1219,7 @@ def find_types():
                     continue
 
                 if issubclass(attr, ResourceManager):
                     continue
 
                 if issubclass(attr, ResourceManager):
-                    types.append(attr)
+                    types.add(attr)
 
                     if not modname in sys.modules:
                         sys.modules[modname] = module
 
                     if not modname in sys.modules:
                         sys.modules[modname] = module
@@ -1070,4 +1233,3 @@ def find_types():
 
     return types
 
 
     return types
 
-