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)