Adding NS3 FDNetDevice RM
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Wed, 26 Nov 2014 12:11:06 +0000 (13:11 +0100)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Wed, 26 Nov 2014 12:11:06 +0000 (13:11 +0100)
src/nepi/execution/attribute.py
src/nepi/execution/ec.py
src/nepi/execution/runner.py
src/nepi/execution/scheduler.py
src/nepi/execution/trace.py
src/nepi/resources/ns3/classes/dsrdsr_routing.py
src/nepi/resources/ns3/classes/fd_net_device.py
src/nepi/resources/ns3/classes/random_walk2d_mobility_model.py
src/nepi/resources/ns3/classes/sta_wifi_mac.py
src/nepi/resources/ns3/ns3wifinetdevice.py
src/nepi/resources/ns3/resource_manager_generator.py

index bf0a853..3afc96f 100644 (file)
@@ -19,6 +19,8 @@
 
 ### Attribute Types
 class Types:
+    """ Allowed types for the Attribute value
+    """
     String  = "STRING"
     Bool    = "BOOL"
     Enumerate    = "ENUM"
@@ -27,8 +29,7 @@ class Types:
 
 ### Attribute Flags
 class Flags:
-    """ Differents flags to characterize an attribute
-
+    """ Flags to characterize the scope of an Attribute
     """
     # Attribute value can not be read (it is hidden to the user) 
     NoRead    = 1 # 1
@@ -57,49 +58,46 @@ class Flags:
 
 
 class Attribute(object):
+    """ An Attribute exposes a configuration parameter of a resource
     """
-    .. class:: Class Args :
 
-        An Attribute reflects a configuration parameter for
-        a particular resource. Attributes might be read only or
-        not.
-      
-        :param name: Name of the attribute
+    def __init__(self, name, help, type = Types.String,
+            flags = None, default = None, allowed = None,
+            range = None, set_hook = None):
+        """
+        :param name: Name of the Attribute
         :type name: str
 
-        :param help: Attribute description
+        :param help: Description of the Attribute
         :type help: str
         
-        :param type: The type expected for the attribute value.
-                     Should be one of Attribute.Types .
+        :param type: The type expected for the Attribute value.
+                     Should be one of Attribute.Types
         :type type: str
 
-        :param flags: Defines attribute behavior (i.e. whether it is read-only,
-                read and write, etc). This parameter should take its values from
-                Attribute.Flags. Flags values can be bitwised.
+        :param flags: Defines Attribute behavior (i.e. whether it is read-only,
+                read and write, etc). This parameter must take its values from
+                Attribute.Flags. Flags values can be bitwised
         :type flags: hex
 
-        :param default: Default value of the attribute
-        :type default: depends on the type of attribute
+        :param default: Default value for the Attribute
+        :type default: Depends on the type of Attribute
         
-        :param allowed: List of values that the attribute can take. 
-                This parameter is only meaningful for Enumerate type attributes.
+        :param allowed: List of values that the Attribute can take. 
+                This parameter is only meaningful for Enumerate type Attributes
         :type allowed: list
         
         :param range: (max, min) tuple with range of possible values for
-                attributes.
+                Attributes.
                 This parameter is only meaningful for Integer or Double type
-                attributes.
+                Attributes
         :type range: (int, int) or (float, float)
         
         :param set_hook: Function that will be executed whenever a new 
-                value is set for the attribute.
+                value is set for the Attribute.
         :type set_hook: function
 
     """
-    def __init__(self, name, help, type = Types.String,
-            flags = None, default = None, allowed = None,
-            range = None, set_hook = None):
         self._name = name
         self._help = help
         self._type = type
@@ -113,41 +111,41 @@ class Attribute(object):
 
     @property
     def name(self):
-        """ Returns the name of the attribute """
+        """ Returns the name of the Attribute """
         return self._name
 
     @property
     def default(self):
-        """ Returns the default value of the attribute """
+        """ Returns the default value of the Attribute """
         return self._default
 
     @property
     def type(self):
-        """ Returns the type of the attribute """
+        """ Returns the type of the Attribute """
         return self._type
 
     @property
     def help(self):
-        """ Returns the help of the attribute """
+        """ Returns the description of the Attribute """
         return self._help
 
     @property
     def flags(self):
-        """ Returns the flags of the attribute """
+        """ Returns the flags of the Attribute """
         return self._flags
 
     @property
     def allowed(self):
-        """ Returns the allowed value for this attribute """
+        """ Returns the set of allowed values for the Attribute """
         return self._allowed
 
     @property
     def range(self):
-        """ Returns the range of the attribute """
+        """ Returns the range of allowed numerical values for the Attribute """
         return self._range
 
     def has_flag(self, flag):
-        """ Returns true if the attribute has the flag 'flag'
+        """ Returns True if the Attribute has the flag 'flag'
 
         :param flag: Flag to be checked
         :type flag: Flags
@@ -155,11 +153,11 @@ class Attribute(object):
         return (self._flags & flag) == flag
 
     def get_value(self):
-        """ Returns the value of the attribute """
+        """ Returns the value of the Attribute """
         return self._value
 
     def set_value(self, value):
-        """ Change the value of the attribute after checking the type """
+        """ Configure a new value for the Attribute """
         valid = True
 
         if self.type == Types.Enumerate:
@@ -193,5 +191,6 @@ class Attribute(object):
 
     @property
     def has_changed(self):
-        """ Returns true if the value has changed from the default """
+        """ Returns True if the value has changed from the default """
         return self.value != self.default
+
index 58ece22..0c92cb1 100644 (file)
@@ -41,7 +41,7 @@ import threading
 import weakref
 
 class FailureLevel(object):
-    """ Describes the system failure state """
+    """ Possible failure states for the experiment """
     OK = 1
     RM_FAILURE = 2
     EC_FAILURE = 3
@@ -49,20 +49,20 @@ class FailureLevel(object):
 class FailureManager(object):
     """ The FailureManager is responsible for handling errors
     and deciding whether an experiment should be aborted or not
-
     """
 
-    def __init__(self, ec):
-        self._ec = weakref.ref(ec)
+    def __init__(self):
+        self._ec = None
         self._failure_level = FailureLevel.OK
         self._abort = False
 
+    def set_ec(self, ec):
+        self._ec = weakref.ref(ec)
+
     @property
     def ec(self):
         """ Returns the ExperimentController associated to this FailureManager 
-        
         """
-        
         return self._ec()
 
     @property
@@ -70,6 +70,15 @@ class FailureManager(object):
         return self._abort
 
     def eval_failure(self, guid):
+        """ Implements failure policy and sets the abort state of the
+        experiment based on the failure state and criticality of
+        the RM
+
+        :param guid: Guid of the RM upon which the failure of the experiment
+            is evaluated
+        :type guid: int
+
+        """
         if self._failure_level == FailureLevel.OK:
             rm = self.ec.get_resource(guid)
             state = rm.state
@@ -85,7 +94,7 @@ class FailureManager(object):
         self._failure_level = FailureLevel.EC_FAILURE
 
 class ECState(object):
-    """ Possible states for an ExperimentController
+    """ Possible states of the ExperimentController
    
     """
     RUNNING = 1
@@ -95,11 +104,6 @@ class ECState(object):
 
 class ExperimentController(object):
     """
-    .. class:: Class Args :
-      
-        :param exp_id: Human readable identifier for the experiment scenario. 
-        :type exp_id: str
-
     .. note::
 
     An experiment, or scenario, is defined by a concrete set of resources,
@@ -162,7 +166,8 @@ class ExperimentController(object):
         return ec
 
     def __init__(self, exp_id = None, local_dir = None, persist = False,
-            add_node_callback = None, add_edge_callback = None, **kwargs):
+            fm = None, add_node_callback = None, add_edge_callback = None, 
+            **kwargs):
         """ ExperimentController entity to model an execute a network 
         experiment.
         
@@ -177,6 +182,10 @@ class ExperimentController(object):
         completion at local_dir
         :type persist: bool
 
+        :param fm: FailureManager object. If None is given, the default 
+            FailureManager class will be used
+        :type fm: FailureManager
+
         :param add_node_callback: Callback to invoke for node instantiation
         when automatic topology creation mode is used 
         :type add_node_callback: function
@@ -238,7 +247,9 @@ class ExperimentController(object):
         self._stop = False
     
         # Entity in charge of managing system failures
-        self._fm = FailureManager(self)
+        if not fm:
+            self._fm = FailureManager()
+        self._fm.set_ec(self)
 
         # EC state
         self._state = ECState.RUNNING
index 21a20cb..064c2e2 100644 (file)
@@ -25,9 +25,9 @@ import os
 import time
 
 class ExperimentRunner(object):
-    """ The ExperimentRunner entity is reponsible of
+    """ The ExperimentRunner entity is responsible of
     re-running an experiment described by an ExperimentController 
-    multiple time.
+    multiple time
 
     """
     def __init__(self):
@@ -36,39 +36,49 @@ class ExperimentRunner(object):
     def run(self, ec, min_runs = 1, max_runs = -1, wait_time = 0, 
             wait_guids = [], compute_metric_callback = None, 
             evaluate_convergence_callback = None ):
-        """ Re-runs a same experiment multiple times
+        """ Run a same experiment independently multiple times, until the 
+        evaluate_convergence_callback function returns True
 
-        :param ec: Experiment description of experiment to run
+        :param ec: Description of experiment to replicate. 
+            The runner takes care of deploying the EC, so ec.deploy()
+            must not be invoked directly before or after invoking
+            runner.run().
         :type ec: ExperimentController
 
-        :param min_runs: Minimum number of repetitions for experiment
+        :param min_runs: Minimum number of times the experiment must be 
+            replicated
         :type min_runs: int
 
-        :param max_runs: Maximum number of repetitions for experiment
+        :param max_runs: Maximum number of times the experiment can be 
+            replicated
         :type max_runs: int
 
-        :param wait_time: Time to wait in seconds between invoking
-            ec.deploy() and ec.release()
+        :param wait_time: Time to wait in seconds on each run between invoking
+            ec.deploy() and ec.release().
         :type wait_time: float
 
-        :param wait_guids: List of guids to pass to ec.wait_finished
-            after invoking ec.deploy()
+        :param wait_guids: List of guids wait for finalization on each run.
+            This list is passed to ec.wait_finished()
         :type wait_guids: list 
 
-        :param compute_metric_callback: function to invoke after each 
-            experiment run, to compute an experiment metric. 
-            It will be invoked with the ec and the run count as arguments,
-            and it must return the metric value(s) computed for the run
+        :param compute_metric_callback: User defined function invoked after 
+            each experiment run to compute a metric. The metric is usually
+            a network measurement obtained from the data collected 
+            during experiment execution.
+            The function is invoked passing the ec and the run number as arguments. 
+            It must return the value for the computed metric(s) (usually a single 
+            numerical value, but it can be several).
 
                 metric = compute_metric_callback(ec, run)
             
         :type compute_metric_callback: function 
 
-        :param evaluate_convergence_callback: function to evaluate whether the 
-            collected metric samples have converged and the experiment runner
-            can stop. It will be invoked with the ec, the run count and the
-            list of collected metric samples as argument, and it must return
-            either True or False
+        :param evaluate_convergence_callback: User defined function invoked after
+            computing the metric on each run, to evaluate the experiment was
+            run enough times. It takes the list of cumulated metrics produced by 
+            the compute_metric_callback up to the current run, and decided 
+            whether the metrics have statistically converged to a meaningful value
+            or not. It must return either True or False. 
 
                 stop = evaluate_convergence_callback(ec, run, metrics)
 
@@ -121,19 +131,24 @@ class ExperimentRunner(object):
 
         return run
 
-    def evaluate_normal_convergence(self, ec, run, samples):
-        if len(samples) == 0:
+    def evaluate_normal_convergence(self, ec, run, metrics):
+        """ Returns True when the confidence interval of the sample mean is
+        less than 5% of the mean value, for a 95% confidence level,
+        assuming normal distribution of the data
+        """
+
+        if len(metrics) == 0:
             msg = "0 samples collected"
             raise RuntimeError, msg
 
-        x = numpy.array(samples)
-        n = len(samples)
+        x = numpy.array(metrics)
+        n = len(metrics)
         std = x.std()
         se = std / math.sqrt(n)
         m = x.mean()
 
-        # confidence interval for 95% confidence level.
-        # Asuming samples are normally distributed
+        # Confidence interval for 95% confidence level, 
+        # assuming normally distributed data.
         ci95 = se * 2
         
         ec.logger.info(" RUN %d - SAMPLES %d MEAN %.2f STD %.2f CI (95%%) %.2f \n" % (
@@ -141,7 +156,11 @@ class ExperimentRunner(object):
 
         return m * 0.05 >= ci95
 
-    def run_experiment(self, filepath, wait_time, wait_guids): 
+    def run_experiment(self, filepath, wait_time, wait_guids):
+        """ Run an experiment based on the description stored
+        in filepath.
+
+        """
         ec = ExperimentController.load(filepath)
 
         ec.deploy()
index cbd9874..7448e5c 100644 (file)
@@ -21,15 +21,26 @@ import itertools
 import heapq
 
 class TaskStatus:
+    """ Execution state of the Task
+    """
     NEW = 0
     DONE = 1
     ERROR = 2
 
 class Task(object):
-    """ This class is to define a task, that is represented by an id,
-    an execution time 'timestamp' and an action 'callback """
+    """ A Task represents an operation to be executed by the 
+    ExperimentController scheduler
+    """
 
     def __init__(self, timestamp, callback):
+        """
+        :param timestamp: Future execution date of the operation
+        :type timestamp: str
+
+        :param callback: A function to invoke in order to execute the operation
+        :type callback: function
+
+        """ 
         self.id = None 
         self.timestamp = timestamp
         self.callback = callback
@@ -37,7 +48,7 @@ class Task(object):
         self.status = TaskStatus.NEW
 
 class HeapScheduler(object):
-    """ Create a Heap Scheduler.
+    """ Create a Heap Scheduler
 
     .. note::
 
@@ -59,9 +70,9 @@ class HeapScheduler(object):
         return self._valid
 
     def schedule(self, task):
-        """ Add the task 'task' in the heap of the scheduler
+        """ Add a task to the queue ordered by task.timestamp and arrival order
 
-        :param task: task that need to be schedule
+        :param task: task to schedule
         :type task: task
         """
         if task.id == None:
@@ -73,10 +84,11 @@ class HeapScheduler(object):
         return task
 
     def remove(self, tid):
-        """ Remove a task form the heap
+        """ Remove a task form the queue
 
-        :param tid: Id of the task that need to be removed
+        :param tid: Id of the task to be removed
         :type tid: int
+
         """
         try:
             self._valid.remove(tid)
@@ -84,8 +96,7 @@ class HeapScheduler(object):
             pass
 
     def next(self):
-        """ Get the next task in the scheduler
-
+        """ Get the next task in the queue by timestamp and arrival order
         """
         while self._queue:
             try:
index 4ad5774..12e93c4 100644 (file)
 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
 
 class TraceAttr:
-    """ Trace attributes represent different information
-    aspects that can be retrieved from a trace.
+    """A Trace attribute defines information about a Trace that can
+    be queried
     """
-    ALL = 'all'
-    STREAM = 'stream'
-    PATH = 'path'
-    SIZE = 'size'
+    ALL = "all"
+    STREAM = "stream"
+    PATH = "path"
+    SIZE = "size"
 
 class Trace(object):
-    """
-    .. class:: Class Args :
-      
-        :param name: Name of the trace
-        :type name: str
-        :param help: Help about the trace
-        :type help: str
-
+    """ A Trace represents information about a Resource that can 
+    be collected 
     """
 
     def __init__(self, name, help, enabled = False):
+        """
+        :param name: Name of the Trace
+        :type name: str
+
+        :param help: Description of the Trace
+        :type help: str
+        
+        :param enabled: Sets activation state of Trace
+        :type enabled: bool
+        """
         self._name = name
         self._help = help
         self.enabled = enabled
index e6f83ff..c8d88bd 100644 (file)
@@ -412,7 +412,7 @@ class NS3dsrDsrRouting(NS3Base):
         attr_linkacknowledgment = Attribute("LinkAcknowledgment",
             "Enable Link layer acknowledgment mechanism",
             type = Types.Bool,
-            default = "False",  
+            default = "True",  
             allowed = None,
             range = None,    
             flags = Flags.Reserved | Flags.Construct)
index 62fc602..4ba2a9f 100644 (file)
@@ -20,10 +20,10 @@ from nepi.execution.attribute import Attribute, Flags, Types
 from nepi.execution.trace import Trace, TraceAttr
 from nepi.execution.resource import ResourceManager, clsinit_copy, \
         ResourceState, reschedule_delay
-from nepi.resources.ns3.ns3netdevice import NS3BaseNetDevice 
+from nepi.resources.ns3.ns3fdnetdevice import NS3BaseFdNetDevice 
 
 @clsinit_copy
-class NS3FdNetDevice(NS3BaseNetDevice):
+class NS3FdNetDevice(NS3BaseFdNetDevice):
     _rtype = "ns3::FdNetDevice"
 
     @classmethod
index ee4095a..3d5773b 100644 (file)
@@ -32,7 +32,7 @@ class NS3RandomWalk2dMobilityModel(NS3BaseMobilityModel):
         attr_bounds = Attribute("Bounds",
             "Bounds of the area to cruise.",
             type = Types.String,
-            default = "0|0|100|100",  
+            default = "0|100|0|100",  
             allowed = None,
             range = None,    
             flags = Flags.Reserved | Flags.Construct)
index 3b2288f..650b8ca 100644 (file)
@@ -59,6 +59,16 @@ class NS3StaWifiMac(NS3BaseWifiMac):
 
         cls._register_attribute(attr_maxmissedbeacons)
 
+        attr_activeprobing = Attribute("ActiveProbing",
+            "If true, we send probe requests. If false, we don\'t. NOTE: if more than one STA in your simulation is using active probing, you should enable it at a different simulation time for each STA, otherwise all the STAs will start sending probes at the same time resulting in collisions. See bug 1060 for more info.",
+            type = Types.Bool,
+            default = "False",  
+            allowed = None,
+            range = None,    
+            flags = Flags.Reserved | Flags.Construct)
+
+        cls._register_attribute(attr_activeprobing)
+
         attr_qossupported = Attribute("QosSupported",
             "This Boolean attribute is set to enable 802.11e/WMM-style QoS support at this STA",
             type = Types.Bool,
index ea700ea..9afa071 100644 (file)
@@ -38,15 +38,7 @@ class NS3BaseWifiNetDevice(NS3BaseNetDevice):
 
     @property
     def _rms_to_wait(self):
-        rms = set()
-        
-        node = self.node
-        rms.add(node)
-
-        ipv4 = node.ipv4
-        if node.ipv4:
-            rms.add(ipv4)
-
+        rms = set([self.node, self.node.ipv4])
         return rms
 
     def _configure_mac_address(self):
index 016cfa7..d04cc06 100644 (file)
 #
 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
 
+#
+# Instructions to automatically generate ns-3 ResourceManagers
 # 
-# Instructions. Run with:
+# Configure the ns-3 enviorment (e.g.):
+#
+#  export PYTHONPATH=~/.nepi/nepi-usr/bin/ns-3/ns-3.20/optimized/build/lib/python/site-packages
+#  export LD_LIBRARY_PATH=~/.nepi/nepi-usr/bin/ns-3/ns-3.20/optimized/build/lib
+#
+# Run the RM generator:
 #
 #  PYTHONPATH=$PYTHONPATH:~/repos/nepi/src python src/nepi/resources/ns3/resource_manager_generator.py
 #
@@ -44,6 +51,7 @@ adapted_types = ["ns3::Node",
         "ns3::ErrorModel",
         "ns3::ErrorRateModel",
         "ns3::Application", 
+        "ns3::FdNetDevice",
         #"ns3::DceApplication", 
         "ns3::NetDevice",
         "ns3::Channel",