X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=src%2Fnepi%2Fexecution%2Fresource.py;h=078b07d114b76dd9d0048eb631969595cf70115c;hb=1df0acb80ba1c737280390c1277a4a751843eac4;hp=d8ab870e476de3ba2d442ec0faf34086c829e1ef;hpb=4896d77f40a611a22f9f1f8f2ae0e63e9008fee1;p=nepi.git
diff --git a/src/nepi/execution/resource.py b/src/nepi/execution/resource.py
index d8ab870e..078b07d1 100644
--- a/src/nepi/execution/resource.py
+++ b/src/nepi/execution/resource.py
@@ -1,28 +1,28 @@
-"""
- 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 .
-
-"""
+#
+# 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 .
+#
+# Author: Alina Quereilhac
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
@@ -31,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
@@ -46,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):
@@ -82,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):
@@ -109,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()
@@ -128,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):
@@ -153,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()
@@ -173,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
@@ -254,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]:
@@ -280,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]:
@@ -372,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)
@@ -534,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):
@@ -545,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
@@ -557,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
@@ -572,25 +612,35 @@ 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 inspect
import nepi.resources
path = os.path.dirname(nepi.resources.__file__)
search_path.add(path)
@@ -617,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)