From: Alina Quereilhac Date: Wed, 26 Nov 2014 12:11:06 +0000 (+0100) Subject: Adding NS3 FDNetDevice RM X-Git-Tag: nepi-3.2.0~55 X-Git-Url: http://git.onelab.eu/?p=nepi.git;a=commitdiff_plain;h=8ef508f12834ed9e8781edd692059b67e945b5f0 Adding NS3 FDNetDevice RM --- diff --git a/src/nepi/execution/attribute.py b/src/nepi/execution/attribute.py index bf0a8536..3afc96f9 100644 --- a/src/nepi/execution/attribute.py +++ b/src/nepi/execution/attribute.py @@ -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 + diff --git a/src/nepi/execution/ec.py b/src/nepi/execution/ec.py index 58ece229..0c92cb14 100644 --- a/src/nepi/execution/ec.py +++ b/src/nepi/execution/ec.py @@ -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 diff --git a/src/nepi/execution/runner.py b/src/nepi/execution/runner.py index 21a20cbd..064c2e2a 100644 --- a/src/nepi/execution/runner.py +++ b/src/nepi/execution/runner.py @@ -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() diff --git a/src/nepi/execution/scheduler.py b/src/nepi/execution/scheduler.py index cbd9874f..7448e5c0 100644 --- a/src/nepi/execution/scheduler.py +++ b/src/nepi/execution/scheduler.py @@ -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: diff --git a/src/nepi/execution/trace.py b/src/nepi/execution/trace.py index 4ad57746..12e93c47 100644 --- a/src/nepi/execution/trace.py +++ b/src/nepi/execution/trace.py @@ -18,26 +18,30 @@ # Author: Alina Quereilhac 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 diff --git a/src/nepi/resources/ns3/classes/dsrdsr_routing.py b/src/nepi/resources/ns3/classes/dsrdsr_routing.py index e6f83ffa..c8d88bd7 100644 --- a/src/nepi/resources/ns3/classes/dsrdsr_routing.py +++ b/src/nepi/resources/ns3/classes/dsrdsr_routing.py @@ -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) diff --git a/src/nepi/resources/ns3/classes/fd_net_device.py b/src/nepi/resources/ns3/classes/fd_net_device.py index 62fc6020..4ba2a9fd 100644 --- a/src/nepi/resources/ns3/classes/fd_net_device.py +++ b/src/nepi/resources/ns3/classes/fd_net_device.py @@ -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 diff --git a/src/nepi/resources/ns3/classes/random_walk2d_mobility_model.py b/src/nepi/resources/ns3/classes/random_walk2d_mobility_model.py index ee4095a8..3d5773bd 100644 --- a/src/nepi/resources/ns3/classes/random_walk2d_mobility_model.py +++ b/src/nepi/resources/ns3/classes/random_walk2d_mobility_model.py @@ -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) diff --git a/src/nepi/resources/ns3/classes/sta_wifi_mac.py b/src/nepi/resources/ns3/classes/sta_wifi_mac.py index 3b2288f8..650b8ca9 100644 --- a/src/nepi/resources/ns3/classes/sta_wifi_mac.py +++ b/src/nepi/resources/ns3/classes/sta_wifi_mac.py @@ -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, diff --git a/src/nepi/resources/ns3/ns3wifinetdevice.py b/src/nepi/resources/ns3/ns3wifinetdevice.py index ea700eae..9afa0718 100644 --- a/src/nepi/resources/ns3/ns3wifinetdevice.py +++ b/src/nepi/resources/ns3/ns3wifinetdevice.py @@ -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): diff --git a/src/nepi/resources/ns3/resource_manager_generator.py b/src/nepi/resources/ns3/resource_manager_generator.py index 016cfa7f..d04cc067 100644 --- a/src/nepi/resources/ns3/resource_manager_generator.py +++ b/src/nepi/resources/ns3/resource_manager_generator.py @@ -17,8 +17,15 @@ # # Author: Alina Quereilhac +# +# 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",