platforms = "Linux, OSX",
packages = [
"nepi",
- "nepi.design",
"nepi.execution",
"nepi.resources",
"nepi.resources.all",
"nepi.resources.omf",
"nepi.resources.planetlab",
"nepi.resources.planetlab.openvswitch",
- "nepi.util"],
+ "nepi.util",
+ "nepi.util.parsers"],
package_dir = {"": "src"},
package_data = {
"nepi.resources.planetlab" : [ "scripts/*.py" ],
+++ /dev/null
-#!/usr/bin/python
+++ /dev/null
-#
-# 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 <http://www.gnu.org/licenses/>.
-#
-#
-# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
-
-from nepi.util import guid
-
-guid_gen = guid.GuidGenerator()
-
-class Attributes(object):
- def __init__(self):
- super(Attributes, self).__init__()
- self._attributes = dict()
-
- def __getattr__(self, name):
- try:
- return self._attributes[name]
- except:
- return super(Attributes, self).__getattribute__(name)
-
- def __setattr__(self, name, value):
- try:
- if value == None:
- old = self._attributes[name]
- del self._attributes[name]
- return old
-
- self._attributes[name] = value
- return value
- except:
- return super(Attributes, self).__setattr__(name, value)
-
-class Connections(object):
- def __init__(self):
- super(Connections, self).__init__()
- self._connections = set()
-
- def __getattr__(self, guid_or_label):
- try:
- for b in self._connections:
- if guid_or_label in [b.guid, b.label]:
- return b
- except:
- return super(Connections, self).__getattribute__(guid_or_label)
-
-class Box(object):
- def __init__(self, label = None, guid = None):
- super(Box, self).__init__()
- self._guid = guid_gen.next(guid)
- self._a = Attributes()
- self._c = Connections()
- self._tags = set()
- self.label = label or self._guid
-
- # Graphical information to draw box
- self.x = 0
- self.y = 0
- self.width = 4
- self.height = 4
-
- @property
- def tags(self):
- return self._tags
-
- @property
- def attributes(self):
- return self._a._attributes.keys()
-
- @property
- def a(self):
- return self._a
-
- @property
- def c(self):
- return self._c
-
- @property
- def guid(self):
- return self._guid
-
- @property
- def connections(self):
- return set(self._c._connections)
-
- def tadd(self, name):
- self._tags.add(name)
-
- def tdel(self, name):
- self._tags.remove(name)
-
- def thas(self, name):
- return name in self._tags
-
- def connect(self, box, cascade = True):
- self._c._connections.add(box)
- if cascade:
- box.connect(self, cascade = False)
-
- def disconnect(self, box, cascade = True):
- self._c._connections.remove(box)
- if cascade:
- box.disconnect(self, cascade = False)
-
- def is_connected(self, box):
- return box in self.connections
-
adequate validation"""
return True
+ @property
def has_changed(self):
""" Returns true if the value has changed from the default """
return self.value != self.default
ResourceState, ResourceState2str
from nepi.execution.scheduler import HeapScheduler, Task, TaskStatus
from nepi.execution.trace import TraceAttr
+from nepi.util.serializer import ECSerializer, SFormats
+from nepi.util.plotter import ECPlotter, PFormats
# TODO: use multiprocessing instead of threading
# TODO: Allow to reconnect to a running experiment instance! (reconnect mode vs deploy mode)
"""
+ @classmethod
+ def load(cls, path, format = SFormats.XML):
+ serializer = ECSerializer()
+ ec = serializer.load(path)
+ return ec
+
def __init__(self, exp_id = None):
super(ExperimentController, self).__init__()
# The runner is a pool of threads used to parallelize
# execution of tasks
- nthreads = int(os.environ.get("NEPI_NTHREADS", "20"))
- self._runner = ParallelRun(maxthreads = nthreads)
+ self._nthreads = 20
+ self._runner = None
# Event processing thread
self._cond = threading.Condition()
"""
return self._run_id
+ @property
+ def nthreads(self):
+ """ Returns the number of processing nthreads used
+
+ """
+ return self._nthreads
+
+
@property
def abort(self):
""" Returns True if the experiment has failed and should be interrupted,
guids.append(guid)
time.sleep(0.5)
-
+
+ def plot(self, fpath = None, format= PFormats.FIGURE, persist = False):
+ plotter = ECPlotter()
+ fpath = plotter.plot(self, fpath = fpath, format= format,
+ persist = persist)
+ return fpath
+
+ def serialize(self, format = SFormats.XML):
+ serializer = ECSerializer()
+ sec = serializer.load(self, format = format)
+ return sec
+
+ def save(self, path, format = SFormats.XML):
+ serializer = ECSerializer()
+ path = serializer.save(self, path, format = format)
+ return path
+
def get_task(self, tid):
""" Returns a task by its id
def get_resource(self, guid):
""" Returns a registered ResourceManager by its guid
- :param guid: Id of the task
+ :param guid: Id of the resource
:type guid: int
:rtype: ResourceManager
rm = self._resources.get(guid)
return rm
+ def get_resources_by_type(self, rtype):
+ """ Returns a registered ResourceManager by its guid
+
+ :param rtype: Resource type
+ :type rtype: string
+
+ :rtype: list of ResourceManagers
+
+ """
+ rms = []
+ for guid, rm in self._resources.iteritems():
+ if rm.get_rtype() == type:
+ rms.append(rm)
+ return rms
+
def remove_resource(self, guid):
del self._resources[guid]
"""
+ self._nthreads = int(os.environ.get("NEPI_NTHREADS", str(self._nthreads)))
+ self._runner = ParallelRun(maxthreads = self.nthreads)
self._runner.start()
while not self._stop:
import threading
import weakref
-reschedule_delay = "1s"
+reschedule_delay = "0.5s"
class ResourceAction:
""" Action that a user can order to a Resource Manager
:rtype: str
"""
attr = self._attrs[name]
- return attr.has_changed()
+ return attr.has_changed
def has_flag(self, name, flag):
""" Returns true if the attribute has the flag 'flag'
--- /dev/null
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.execution.ec import ExperimentController
+
+import math
+import numpy
+import os
+import tempfile
+import time
+
+class ExperimentRunner(object):
+ """ The ExperimentRunner entity is reponsible of
+ re-running an experiment described by an ExperimentController
+ multiple time.
+
+ """
+ def __init__(self):
+ super(ExperimentRunner, self).__init__()
+
+ 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
+
+ :param ec: Experiment description of experiment to run
+ :type name: ExperimentController
+ :rtype: EperimentController
+
+ :param min_runs: Minimum number of repetitions for experiment
+ :type name: int
+ :rtype: int
+
+ :param max_runs: Maximum number of repetitions for experiment
+ :type name: int
+ :rtype: int
+
+ :param wait_time: Time to wait in seconds between invoking
+ ec.deploy() and ec.release()
+ :type name: float
+ :rtype: float
+
+ :param wait_guids: List of guids to pass to ec.wait_finished
+ after invoking ec.deploy()
+ :type name: list
+ :rtype: list of int
+
+ :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 a numeric value for the computed metric:
+
+ metric = compute_metric_callback(ec, run)
+
+ :type name: function
+ :rtype: 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:
+
+ stop = evaluate_convergence_callback(ec, run, metrics)
+
+ If stop is True, then the runner will exit.
+
+ :type name: function
+ :rtype: function
+
+ """
+
+ if (not max_runs or max_runs < 0) and not compute_metric_callback:
+ msg = "Undefined STOP condition, set stop_callback or max_runs"
+ raise RuntimeError, msg
+
+ if compute_metric_callback and not evaluate_convergence_callback:
+ evaluate_convergence_callback = self.evaluate_normal_convergence
+ ec.logger.info(" Treating data as normal to evaluate convergence. "
+ "Experiment will stop when the standard error with 95% "
+ "confidence interval is >= 5% of the mean of the collected samples ")
+
+ # Set useRunId = True in Collectors to make sure results are
+ # independently stored.
+ collectors = ec.get_resources_by_type("Collector")
+ for collector in collectors:
+ collector.set("useRunId", True)
+
+ dirpath = tempfile.mkdtemp()
+ filepath = ec.save(dirpath)
+
+ samples = []
+ run = 0
+ while True:
+ run += 1
+
+ ec = self.run_experiment(filepath, wait_time, wait_guids)
+
+ ec.logger.info(" RUN %d \n" % run)
+
+ if run >= min_runs and max_runs > -1 and run >= max_runs :
+ break
+
+ if compute_metric_callback:
+ metric = compute_metric_callback(ec, run)
+ if metric is not None:
+ samples.append(metric)
+
+ if run >= min_runs and evaluate_convergence_callback:
+ if evaluate_convergence_callback(ec, run, samples):
+ break
+ del ec
+
+ return run
+
+ def evaluate_normal_convergence(self, ec, run, samples):
+ if len(samples) == 0:
+ msg = "0 samples collected"
+ raise RuntimeError, msg
+
+ x = numpy.array(samples)
+ n = len(samples)
+ std = x.std()
+ se = std / math.sqrt(n)
+ m = x.mean()
+ se95 = se * 2
+
+ ec.logger.info(" RUN %d - SAMPLES %d MEAN %.2f STD %.2f SE95%% %.2f \n" % (
+ run, n, m, std, se95 ) )
+
+ return m * 0.05 >= se95
+
+ def run_experiment(self, filepath, wait_time, wait_guids):
+ ec = ExperimentController.load(filepath)
+
+ ec.deploy()
+
+ ec.wait_finished(wait_guids)
+ time.sleep(wait_time)
+
+ ec.release()
+
+ return ec
+
+
@clsinit_copy
class Collector(ResourceManager):
- """ The collector is reponsible of collecting traces
+ """ The collector entity is reponsible of collecting traces
of a same type associated to RMs into a local directory.
.. class:: Class Args :
self._uuid = None
self._connected = set()
self._trace_filename = dict()
+ self._node = None
@property
def connected(self):
@property
def node(self):
- from nepi.resources.ns3.ns3node import NS3BaseNode
- nodes = self.get_connected(NS3BaseNode.get_rtype())
- if nodes: return nodes[0]
- return None
+ if not self._node:
+ from nepi.resources.ns3.ns3node import NS3BaseNode
+ nodes = self.get_connected(NS3BaseNode.get_rtype())
+ if nodes: self._nodes[0]
+
+ return self._node
def trace(self, name, attr = TraceAttr.ALL, block = 512, offset = 0):
filename = self._trace_filename.get(name)
kwargs = dict()
for attr in self._attrs.values():
- if not ( attr.has_flag(Flags.Construct) and attr.has_changed() ):
+ if not ( attr.has_flag(Flags.Construct) and attr.has_changed ):
continue
kwargs[attr.name] = attr._value
class NS3BaseNode(NS3Base):
_rtype = "abstract::ns3::Node"
+ def __init__(self, ec, guid):
+ super(NS3BaseNode, self).__init__(ec, guid)
+ self._simulation = None
+ self._ipv4 = None
+ self._arp = None
+ self._mobility = None
+ self._devices = None
+ self._dceapplications = None
+
@classmethod
def _register_attributes(cls):
enablestack = Attribute("enableStack",
@property
def simulation(self):
- from nepi.resources.ns3.ns3simulation import NS3Simulation
- for guid in self.connections:
- rm = self.ec.get_resource(guid)
- if isinstance(rm, NS3Simulation):
- return rm
-
- msg = "Node not connected to simulation"
- self.error(msg)
- raise RuntimeError, msg
-
+ if not self._simulation:
+ from nepi.resources.ns3.ns3simulation import NS3Simulation
+ for guid in self.connections:
+ rm = self.ec.get_resource(guid)
+ if isinstance(rm, NS3Simulation):
+ self._simulation = rm
+
+ if not self._simulation:
+ msg = "Node not connected to simulation"
+ self.error(msg)
+ raise RuntimeError, msg
+
+ return self._simulation
+
@property
def ipv4(self):
- from nepi.resources.ns3.ns3ipv4l3protocol import NS3BaseIpv4L3Protocol
- ipv4s = self.get_connected(NS3BaseIpv4L3Protocol.get_rtype())
- if ipv4s: return ipv4s[0]
- return None
+ if not self._ipv4:
+ from nepi.resources.ns3.ns3ipv4l3protocol import NS3BaseIpv4L3Protocol
+ ipv4s = self.get_connected(NS3BaseIpv4L3Protocol.get_rtype())
+ if ipv4s:
+ self._ipv4 = ipv4s[0]
+
+ return self._ipv4
@property
def arp(self):
- from nepi.resources.ns3.ns3arpl3protocol import NS3BaseArpL3Protocol
- arps = self.get_connected(NS3BaseArpL3Protocol.get_rtype())
- if arps: return arps[0]
- return None
+ if not self._arp:
+ from nepi.resources.ns3.ns3arpl3protocol import NS3BaseArpL3Protocol
+ arps = self.get_connected(NS3BaseArpL3Protocol.get_rtype())
+ if arps:
+ self._arp = arps[0]
+
+ return self._arp
@property
def mobility(self):
- from nepi.resources.ns3.ns3mobilitymodel import NS3BaseMobilityModel
- mobility = self.get_connected(NS3BaseMobilityModel.get_rtype())
- if mobility: return mobility[0]
- return None
+ if not self._mobility:
+ from nepi.resources.ns3.ns3mobilitymodel import NS3BaseMobilityModel
+ mobility = self.get_connected(NS3BaseMobilityModel.get_rtype())
+ if mobility:
+ self._mobility = mobility[0]
+
+ return self._mobility
@property
def devices(self):
- from nepi.resources.ns3.ns3netdevice import NS3BaseNetDevice
- devices = self.get_connected(NS3BaseNetDevice.get_rtype())
+ if not self._devices:
+ from nepi.resources.ns3.ns3netdevice import NS3BaseNetDevice
+ devices = self.get_connected(NS3BaseNetDevice.get_rtype())
+
+ if not devices:
+ msg = "Node not connected to devices"
+ self.error(msg)
+ raise RuntimeError, msg
- if not devices:
- msg = "Node not connected to devices"
- self.error(msg)
- raise RuntimeError, msg
+ self._devices = devices
- return devices
+ return self._devices
@property
def dceapplications(self):
- from nepi.resources.ns3.ns3dceapplication import NS3BaseDceApplication
- dceapplications = self.get_connected(NS3BaseDceApplication.get_rtype())
+ if not self._dceapplications:
+ from nepi.resources.ns3.ns3dceapplication import NS3BaseDceApplication
+ self._dceapplications = self.get_connected(NS3BaseDceApplication.get_rtype())
- return dceapplications
+ return self._dceapplications
@property
def _rms_to_wait(self):
+++ /dev/null
-#
-# 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 <http://www.gnu.org/licenses/>.
-#
-# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
-
-from nepi.design.box import Box
-
-from xml.dom import minidom
-import sys
-
-STRING = "string"
-BOOL = "bool"
-INTEGER = "integer"
-DOUBLE = "float"
-
-def xmlencode(s):
- if isinstance(s, str):
- rv = s.decode("latin1")
- elif not isinstance(s, unicode):
- rv = unicode(s)
- else:
- rv = s
- return rv.replace(u'\x00',u'�')
-
-def xmldecode(s):
- return s.replace(u'�',u'\x00').encode("utf8")
-
-def from_type(value):
- if isinstance(value, str):
- return STRING
- if isinstance(value, bool):
- return BOOL
- if isinstance(value, int):
- return INTEGER
- if isinstance(value, float):
- return DOUBLE
-
-def to_type(type, value):
- if type == STRING:
- return str(value)
- if type == BOOL:
- return value == "True"
- if type == INTEGER:
- return int(value)
- if type == DOUBLE:
- return float(value)
-
-class XMLParser(object):
- def to_xml(self, box):
- doc = minidom.Document()
-
- root = doc.createElement("boxes")
- doc.appendChild(root)
-
- traversed = dict()
- self._traverse_boxes(doc, traversed, box)
-
- # Keep the order
- for guid in sorted(traversed.keys()):
- bnode = traversed[guid]
- root.appendChild(bnode)
-
- try:
- xml = doc.toprettyxml(indent=" ", encoding="UTF-8")
- except:
- print >>sys.stderr, "Oops: generating XML from %s" % (data,)
- raise
-
- return xml
-
- def _traverse_boxes(self, doc, traversed, box):
- bnode = doc.createElement("box")
- bnode.setAttribute("guid", xmlencode(box.guid))
- bnode.setAttribute("label", xmlencode(box.label))
- bnode.setAttribute("x", xmlencode(box.x))
- bnode.setAttribute("y", xmlencode(box.y))
- bnode.setAttribute("width", xmlencode(box.width))
- bnode.setAttribute("height", xmlencode(box.height))
-
- traversed[box.guid] = bnode
-
- anode = doc.createElement("attributes")
- bnode.appendChild(anode)
- for name in sorted(box.attributes):
- value = getattr(box.a, name)
- aanode = doc.createElement("attribute")
- anode.appendChild(aanode)
- aanode.setAttribute("name", xmlencode(name))
- aanode.setAttribute("value", xmlencode(value))
- aanode.setAttribute("type", from_type(value))
-
- tnode = doc.createElement("tags")
- bnode.appendChild(tnode)
- for tag in sorted(box.tags):
- ttnode = doc.createElement("tag")
- tnode.appendChild(ttnode)
- ttnode.setAttribute("name", xmlencode(tag))
-
- cnode = doc.createElement("connections")
- bnode.appendChild(cnode)
- for b in sorted(box.connections):
- ccnode = doc.createElement("connection")
- cnode.appendChild(ccnode)
- ccnode.setAttribute("guid", xmlencode(b.guid))
- if b.guid not in traversed:
- self._traverse_boxes(doc, traversed, b)
-
- def from_xml(self, xml):
- doc = minidom.parseString(xml)
- bnode_list = doc.getElementsByTagName("box")
-
- boxes = dict()
- connections = dict()
-
- for bnode in bnode_list:
- if bnode.nodeType == doc.ELEMENT_NODE:
- guid = int(bnode.getAttribute("guid"))
- label = xmldecode(bnode.getAttribute("label"))
- x = float(bnode.getAttribute("x"))
- y = float(bnode.getAttribute("y"))
- height = float(bnode.getAttribute("height"))
- width = float(bnode.getAttribute("width"))
- box = Box(label=label, guid=guid)
- boxes[guid] = box
-
- anode_list = bnode.getElementsByTagName("attribute")
- for anode in anode_list:
- name = xmldecode(anode.getAttribute("name"))
- value = xmldecode(anode.getAttribute("value"))
- type = xmldecode(anode.getAttribute("type"))
- value = to_type(type, value)
- setattr(box.a, name, value)
-
- tnode_list = bnode.getElementsByTagName("tag")
- for tnode in tnode_list:
- value = xmldecode(tnode.getAttribute("name"))
- box.tadd(value)
-
- connections[box] = set()
- cnode_list = bnode.getElementsByTagName("connection")
- for cnode in cnode_list:
- guid = int(cnode.getAttribute("guid"))
- connections[box].add(guid)
-
- for box, conns in connections.iteritems():
- for guid in conns:
- b = boxes[guid]
- box.connect(b)
-
- return box
-
--- /dev/null
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.util.timefuncs import stformat, tsformat
+
+from xml.dom import minidom
+
+import datetime
+import sys
+import os
+
+STRING = "string"
+BOOL = "bool"
+INTEGER = "integer"
+DOUBLE = "float"
+
+def xmlencode(s):
+ if isinstance(s, str):
+ rv = s.decode("latin1")
+ if isinstance(s, datetime.datetime):
+ rv = tsformat(s)
+ elif not isinstance(s, unicode):
+ rv = unicode(s)
+ else:
+ rv = s
+ return rv.replace(u'\x00',u'�')
+
+def xmldecode(s, cast = str):
+ ret = s.replace(u'�',u'\x00').encode("ascii")
+ ret = cast(ret)
+ if s == "None":
+ return None
+ return ret
+
+def from_type(value):
+ if isinstance(value, bool):
+ return BOOL
+ if isinstance(value, int):
+ return INTEGER
+ if isinstance(value, float):
+ return DOUBLE
+
+ return STRING
+
+def to_type(type, value):
+ if not value:
+ return value
+
+ if type == STRING:
+ return str(value)
+ if type == BOOL:
+ return value == "True"
+ if type == INTEGER:
+ return int(value)
+ if type == DOUBLE:
+ return float(value)
+
+class ECXMLParser(object):
+ def to_xml(self, ec):
+
+ doc = minidom.Document()
+
+ self._ec_to_xml(doc, ec)
+
+ try:
+ xml = doc.toprettyxml(indent=" ", encoding="UTF-8")
+ except:
+ print >>sys.stderr, "Oops: generating XML from %s" % (data,)
+ raise
+
+ return xml
+
+ def _ec_to_xml(self, doc, ec):
+ ecnode = doc.createElement("experiment")
+ ecnode.setAttribute("exp_id", xmlencode(ec.exp_id))
+ ecnode.setAttribute("run_id", xmlencode(ec.run_id))
+ ecnode.setAttribute("nthreads", xmlencode(ec.nthreads))
+ doc.appendChild(ecnode)
+
+ for guid, rm in ec._resources.iteritems():
+ self._rm_to_xml(doc, ecnode, ec, guid, rm)
+
+ return doc
+
+ def _rm_to_xml(self, doc, ecnode, ec, guid, rm):
+ rmnode = doc.createElement("rm")
+ rmnode.setAttribute("guid", xmlencode(guid))
+ rmnode.setAttribute("rtype", xmlencode(rm._rtype))
+ rmnode.setAttribute("state", xmlencode(rm._state))
+ if rm._start_time:
+ rmnode.setAttribute("start_time", xmlencode(rm._start_time))
+ if rm._stop_time:
+ rmnode.setAttribute("stop_time", xmlencode(rm._stop_time))
+ if rm._discover_time:
+ rmnode.setAttribute("discover_time", xmlencode(rm._discover_time))
+ if rm._provision_time:
+ rmnode.setAttribute("provision_time", xmlencode(rm._provision_time))
+ if rm._ready_time:
+ rmnode.setAttribute("ready_time", xmlencode(rm._ready_time))
+ if rm._release_time:
+ rmnode.setAttribute("release_time", xmlencode(rm._release_time))
+ if rm._failed_time:
+ rmnode.setAttribute("failed_time", xmlencode(rm._failed_time))
+ ecnode.appendChild(rmnode)
+
+ anode = doc.createElement("attributes")
+ attributes = False
+
+ for attr in rm._attrs.values():
+ if attr.has_changed:
+ attributes = True
+ aanode = doc.createElement("attribute")
+ aanode.setAttribute("name", xmlencode(attr.name))
+ aanode.setAttribute("value", xmlencode(attr.value))
+ aanode.setAttribute("type", from_type(attr.value))
+ anode.appendChild(aanode)
+
+ if attributes:
+ rmnode.appendChild(anode)
+
+ cnode = doc.createElement("connections")
+ connections = False
+
+ for guid in rm._connections:
+ connections = True
+ ccnode = doc.createElement("connection")
+ ccnode.setAttribute("guid", xmlencode(guid))
+ cnode.appendChild(ccnode)
+
+ if connections:
+ rmnode.appendChild(cnode)
+
+ cnnode = doc.createElement("conditions")
+ conditions = False
+
+ for action, conds in rm._conditions.iteritems():
+ conditions = True
+ for (group, state, time) in conds:
+ ccnnode = doc.createElement("condition")
+ ccnnode.setAttribute("action", xmlencode(action))
+ ccnnode.setAttribute("group", xmlencode(group))
+ ccnnode.setAttribute("state", xmlencode(state))
+ ccnnode.setAttribute("time", xmlencode(time))
+ cnnode.appendChild(ccnnode)
+
+ if conditions:
+ rmnode.appendChild(cnnode)
+
+ tnode = doc.createElement("traces")
+ traces = False
+
+ for trace in rm._trcs.values():
+ if trace.enabled:
+ traces = True
+ ttnode = doc.createElement("trace")
+ ttnode.setAttribute("name", xmlencode(trace.name))
+ tnode.appendChild(ttnode)
+
+ if traces:
+ rmnode.appendChild(tnode)
+
+ def from_xml(self, xml):
+ doc = minidom.parseString(xml)
+ return self._ec_from_xml(doc)
+
+ def _ec_from_xml(self, doc):
+ from nepi.execution.ec import ExperimentController
+ ec = None
+
+ ecnode_list = doc.getElementsByTagName("experiment")
+ for ecnode in ecnode_list:
+ if ecnode.nodeType == doc.ELEMENT_NODE:
+ exp_id = xmldecode(ecnode.getAttribute("exp_id"))
+ run_id = xmldecode(ecnode.getAttribute("run_id"))
+ nthreads = xmldecode(ecnode.getAttribute("nthreads"))
+
+ os.environ["NEPI_NTHREADS"] = nthreads
+ ec = ExperimentController(exp_id = exp_id)
+
+ connections = set()
+
+ rmnode_list = ecnode.getElementsByTagName("rm")
+ for rmnode in rmnode_list:
+ if rmnode.nodeType == doc.ELEMENT_NODE:
+ self._rm_from_xml(doc, rmnode, ec, connections)
+
+ for (guid1, guid2) in connections:
+ ec.register_connection(guid1, guid2)
+
+ break
+
+ return ec
+
+ def _rm_from_xml(self, doc, rmnode, ec, connections):
+ start_time = None
+ stop_time = None
+ discover_time = None
+ provision_time = None
+ ready_time = None
+ release_time = None
+ failed_time = None
+
+ guid = xmldecode(rmnode.getAttribute("guid"), int)
+ rtype = xmldecode(rmnode.getAttribute("rtype"))
+
+ # FOR NOW ONLY STATE NEW IS ALLOWED
+ state = 0
+ """
+ state = xmldecode(rmnode.getAttribute("state"), int)
+
+ if rmnode.hasAttribute("start_time"):
+ start_time = xmldecode(rmnode.getAttribute("start_time"),
+ datetime.datetime)
+ if rmnode.hasAttribute("stop_time"):
+ stop_time = xmldecode(rmnode.getAttribute("stop_time"),
+ datetime.datetime)
+ if rmnode.hasAttribute("discover_time"):
+ dicover_time = xmldecode(rmnode.getAttribute("discover_time"),
+ datetime.datetime)
+ if rmnode.hasAttribute("provision_time"):
+ provision_time = xmldecode(rmnode.getAttribute("provision_time"),
+ datetime.datetime)
+ if rmnode.hasAttribute("ready_time"):
+ ready_time = xmldecode(rmnode.getAttribute("ready_time"),
+ datetime.datetime)
+ if rmnode.hasAttribute("release_time"):
+ release_time = xmldecode(rmnode.getAttribute("release_time"),
+ datetime.datetime)
+ if rmnode.hasAttribute("failed_time"):
+ failed_time = xmldecode(rmnode.getAttribute("failed_time"),
+ datetime.datetime)
+ """
+
+ ec.register_resource(rtype, guid = guid)
+ rm = ec.get_resource(guid)
+ rm.set_state_time(state, "_start_time", start_time)
+ rm.set_state_time(state, "_stop_time", stop_time)
+ rm.set_state_time(state, "_discover_time", discover_time)
+ rm.set_state_time(state, "_provision_time", provision_time)
+ rm.set_state_time(state, "_ready_time", ready_time)
+ rm.set_state_time(state, "_release_time", release_time)
+ rm.set_state_time(state, "_failed_time", failed_time)
+
+ anode_list = rmnode.getElementsByTagName("attributes")
+ if anode_list:
+ aanode_list = anode_list[0].getElementsByTagName("attribute")
+ for aanode in aanode_list:
+ name = xmldecode(aanode.getAttribute("name"))
+ value = xmldecode(aanode.getAttribute("value"))
+ tipe = xmldecode(aanode.getAttribute("type"))
+ value = to_type(tipe, value)
+ rm.set(name, value)
+
+ cnode_list = rmnode.getElementsByTagName("connections")
+ if cnode_list:
+ ccnode_list = cnode_list[0].getElementsByTagName("connection")
+ for ccnode in ccnode_list:
+ guid2 = xmldecode(ccnode.getAttribute("guid"), int)
+ connections.add((guid, guid2))
+
+ tnode_list = rmnode.getElementsByTagName("traces")
+ if tnode_list:
+ ttnode_list = tnode_list[0].getElementsByTagName("trace")
+ for ttnode in ttnode_list:
+ name = xmldecode(ttnode.getAttribute("name"))
+ ec.enable_trace(guid, name)
+
+ cnnode_list = rmnode.getElementsByTagName("conditions")
+ if cnnode_list:
+ ccnnode_list = cnnode_list[0].getElementsByTagName("condition")
+ for ccnnode in ccnnode_list:
+ action = xmldecode(ccnnode.getAttribute("action"), int)
+ group = xmldecode(ccnnode.getAttribute("group"), eval)
+ state = xmldecode(ccnnode.getAttribute("state"), int)
+ time = xmldecode(ccnnode.getAttribute("time"))
+ time = to_type('STRING', time)
+ ec.register_condition(guid, action, group, state, time = time)
+
--- /dev/null
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+import networkx
+import os
+
+class PFormats:
+ DOT = "dot"
+ FIGURE = "figure"
+
+class ECPlotter(object):
+ def plot(self, ec, fpath = None, format= PFormats.FIGURE, persist = False):
+ graph, labels = self._ec2graph(ec)
+
+ add_extension = False
+
+ if persist and not fpath:
+ import tempfile
+ dirpath = tempfile.mkdtemp()
+ fpath = os.path.join(dirpath, "%s_%s" % (ec.exp_id, ec.run_id))
+ add_extension = True
+
+ if format == PFormats.FIGURE:
+ import matplotlib.pyplot as plt
+ pos = networkx.graphviz_layout(graph, prog="neato")
+ networkx.draw(graph, pos = pos, node_color="white",
+ node_size = 500, with_labels=True)
+
+ label = "\n".join(map(lambda v: "%s: %s" % (v[0], v[1]), labels.iteritems()))
+ plt.annotate(label, xy=(0.0, 0.95), xycoords='axes fraction')
+
+ if persist:
+ if add_extension:
+ fpath += ".png"
+
+ plt.savefig(fpath, bbox_inches="tight")
+ else:
+ plt.show()
+
+ elif format == PFormats.DOT:
+ if persist:
+ if add_extension:
+ fpath += ".dot"
+
+ networkx.write_dot(graph, fpath)
+ else:
+ import subprocess
+ subprocess.call(["dot", "-Tps", fpath, "-o", "%s.ps" % fpath])
+ subprocess.call(["evince","%s.ps" % fpath])
+
+ return fpath
+
+ def _ec2graph(self, ec):
+ graph = networkx.Graph(graph = dict(overlap = "false"))
+
+ labels = dict()
+ connections = set()
+
+ for guid, rm in ec._resources.iteritems():
+ label = rm.get_rtype()
+
+ graph.add_node(guid,
+ label = "%d %s" % (guid, label),
+ width = 50/72.0, # 1 inch = 72 points
+ height = 50/72.0,
+ shape = "circle")
+
+ labels[guid] = label
+
+ for guid2 in rm.connections:
+ # Avoid adding a same connection twice
+ if (guid2, guid) not in connections:
+ connections.add((guid, guid2))
+
+ for (guid1, guid2) in connections:
+ graph.add_edge(guid1, guid2)
+
+ return graph, labels
--- /dev/null
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+import datetime
+import os
+
+class SFormats:
+ XML = "xml"
+
+class ECSerializer(object):
+ def load(self, path, format = SFormats.XML):
+ if format == SFormats.XML:
+ from nepi.util.parsers.xml_parser import ECXMLParser
+
+ parser = ECXMLParser()
+ f = open(path, "r")
+ xml = f.read()
+ f.close()
+
+ ec = parser.from_xml(xml)
+
+ return ec
+
+ def serialize(self, ec, format = SFormats.XML):
+ if format == SFormats.XML:
+ from nepi.util.parsers.xml_parser import ECXMLParser
+
+ parser = ECXMLParser()
+ sec = parser.to_xml(ec)
+
+ return sec
+
+ def save(self, ec, path, format = SFormats.XML):
+ date = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
+ filename = "%s_%s" % (ec.exp_id, date)
+
+ if format == SFormats.XML:
+ path = os.path.join(path, "%s.xml" % filename)
+ sec = self.serialize(ec, format = format)
+ f = open(path, "w")
+ f.write(sec)
+ f.close()
+
+ return path
+
+++ /dev/null
-#!/usr/bin/env python
-#
-# 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 <http://www.gnu.org/licenses/>.
-#
-# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
-
-
-from nepi.design.box import Box
-
-import unittest
-
-class BoxDesignTestCase(unittest.TestCase):
- def test_simple_design(self):
- node1 = Box()
- node2 = Box()
-
- node1.label = "uno"
- node2.label = "dos"
-
- node1.tadd('nodo')
- node2.tadd('mynodo')
-
- self.assertEquals(node1.tags, set(['nodo']))
- self.assertEquals(node2.tags, set(['mynodo']))
-
- node1.a.hola = "chau"
- node2.a.hello = "bye"
-
- self.assertEquals(node1.a.hola, "chau")
- self.assertEquals(node2.a.hello, "bye")
-
- node1.connect(node2)
-
- self.assertEquals(node1.connections, set([node2]))
- self.assertEquals(node2.connections, set([node1]))
- self.assertTrue(node1.is_connected(node2))
- self.assertTrue(node2.is_connected(node1))
-
- self.assertEquals(node1.c.dos.a.hello, "bye")
- self.assertEquals(node2.c.uno.a.hola, "chau")
-
- node2.disconnect(node1)
-
- self.assertEquals(node1.connections, set([]))
- self.assertEquals(node2.connections, set([]))
- self.assertFalse(node1.is_connected(node2))
- self.assertFalse(node2.is_connected(node1))
-
- self.assertRaises(AttributeError, node1.c.dos)
- self.assertRaises(AttributeError, node2.c.uno)
-
-
-if __name__ == '__main__':
- unittest.main()
-
--- /dev/null
+#!/usr/bin/env python
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.execution.ec import ExperimentController
+from nepi.execution.resource import ResourceManager, ResourceState, \
+ clsinit_copy, ResourceAction, ResourceFactory
+from nepi.execution.runner import ExperimentRunner
+
+import functools
+import os
+import shutil
+import tempfile
+import time
+import unittest
+
+reschedule_delay = "0.5s"
+deploy_time = 0
+run_time = 0
+
+class Link(ResourceManager):
+ _rtype = "dummy::Link"
+ def do_deploy(self):
+ time.sleep(deploy_time)
+ super(Link, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+class Interface(ResourceManager):
+ _rtype = "dummy::Interface"
+
+ def do_deploy(self):
+ node = self.get_connected(Node.get_rtype())[0]
+ link = self.get_connected(Link.get_rtype())[0]
+
+ if node.state < ResourceState.READY or \
+ link.state < ResourceState.READY:
+ self.ec.schedule(reschedule_delay, self.deploy)
+ self.logger.debug(" -------- RESCHEDULING ------- ")
+ else:
+ time.sleep(deploy_time)
+ super(Interface, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+class Node(ResourceManager):
+ _rtype = "dummy::Node"
+
+ def do_deploy(self):
+ self.logger.debug(" -------- DO_DEPLOY ------- ")
+ time.sleep(deploy_time)
+ super(Node, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+class Application(ResourceManager):
+ _rtype = "dummy::Application"
+
+ def do_deploy(self):
+ node = self.get_connected(Node.get_rtype())[0]
+
+ if node.state < ResourceState.READY:
+ self.ec.schedule(reschedule_delay, self.deploy)
+ self.logger.debug(" -------- RESCHEDULING ------- ")
+ else:
+ time.sleep(deploy_time)
+ super(Application, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+ def do_start(self):
+ super(Application, self).do_start()
+ time.sleep(run_time)
+ self.ec.schedule("0s", self.stop)
+
+ResourceFactory.register_type(Application)
+ResourceFactory.register_type(Node)
+ResourceFactory.register_type(Interface)
+ResourceFactory.register_type(Link)
+
+class RunnerTestCase(unittest.TestCase):
+ def test_runner_max_runs(self):
+ node_count = 4
+ app_count = 2
+
+ ec = ExperimentController(exp_id = "max-runs-test")
+
+ # Add simulated nodes and applications
+ nodes = list()
+ apps = list()
+ ifaces = list()
+
+ for i in xrange(node_count):
+ node = ec.register_resource("dummy::Node")
+ nodes.append(node)
+
+ iface = ec.register_resource("dummy::Interface")
+ ec.register_connection(node, iface)
+ ifaces.append(iface)
+
+ for i in xrange(app_count):
+ app = ec.register_resource("dummy::Application")
+ ec.register_connection(node, app)
+ apps.append(app)
+
+ link = ec.register_resource("dummy::Link")
+
+ for iface in ifaces:
+ ec.register_connection(link, iface)
+
+ rnr = ExperimentRunner()
+ runs = rnr.run(ec, min_runs = 5, max_runs = 10, wait_guids = apps,
+ wait_time = 0)
+
+ self.assertEquals(runs, 10)
+
+ def test_runner_convergence(self):
+ node_count = 4
+ app_count = 2
+
+ ec = ExperimentController(exp_id = "convergence-test")
+
+ # Add simulated nodes and applications
+ nodes = list()
+ apps = list()
+ ifaces = list()
+
+ for i in xrange(node_count):
+ node = ec.register_resource("dummy::Node")
+ nodes.append(node)
+
+ iface = ec.register_resource("dummy::Interface")
+ ec.register_connection(node, iface)
+ ifaces.append(iface)
+
+ for i in xrange(app_count):
+ app = ec.register_resource("dummy::Application")
+ ec.register_connection(node, app)
+ apps.append(app)
+
+ link = ec.register_resource("dummy::Link")
+
+ for iface in ifaces:
+ ec.register_connection(link, iface)
+
+ samples = [10, 10, 10, 10, 12, 10, 12, 10, 10, 11]
+
+ def compute_metric_callback(samples, ec, run):
+ return samples[run-1]
+
+ metric_callback = functools.partial(compute_metric_callback, samples)
+
+ rnr = ExperimentRunner()
+ runs = rnr.run(ec, min_runs = 5,
+ compute_metric_callback = metric_callback,
+ wait_guids = apps,
+ wait_time = 0)
+
+ self.assertEquals(runs, 10)
+
+if __name__ == '__main__':
+ unittest.main()
+
ec.register_connection(app, node)
+
ec.deploy()
ec.wait_finished([app])
--- /dev/null
+#!/usr/bin/env python
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.execution.ec import ExperimentController
+from nepi.execution.runner import ExperimentRunner
+
+from test_utils import skipIfNotAlive, skipInteractive
+
+import functools
+import glob
+import os
+import re
+import shutil
+import time
+import tempfile
+import unittest
+
+_ping_re = re.compile("[^/]+rtt min/avg/max/mdev = (?P<min>\d\.\d+)/(?P<avg>\d\.\d+)/(?P<max>\d\.\d+)/(?P<mdev>\d\.\d+)[^/]+", re.MULTILINE)
+
+class LinuxMultiRunTestCase(unittest.TestCase):
+ def setUp(self):
+ self.fedora_host = "nepi2.pl.sophia.inria.fr"
+ self.fedora_user = "inria_nepi"
+
+ self.ubuntu_host = "roseval.pl.sophia.inria.fr"
+ self.ubuntu_user = "inria_nepi"
+
+ self.target = "nepi5.pl.sophia.inria.fr"
+
+ @skipIfNotAlive
+ def t_simple_multirun(self, host, user, depends):
+
+ dirpath = tempfile.mkdtemp()
+
+ ec = ExperimentController(exp_id="test-condition-multirun")
+
+ node = ec.register_resource("LinuxNode")
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "cleanHome", True)
+ ec.set(node, "cleanProcesses", True)
+
+ ping = ec.register_resource("LinuxApplication")
+ ec.set(ping, "command", "ping -c10 nepi.inria.fr")
+ ec.register_connection(ping, node)
+
+ collector = ec.register_resource("Collector")
+ ec.set(collector, "traceName", "stdout")
+ ec.set(collector, "storeDir", dirpath)
+ ec.set(collector, "useRunId", True)
+ ec.register_connection(ping, collector)
+
+ def compute_metric_callback(ping, ec, run):
+ stdout = ec.trace(ping, "stdout")
+
+ m = _ping_re.match(stdout)
+ if not m:
+ return None
+
+ return float(m.groupdict()["min"])
+
+ metric_callback = functools.partial(compute_metric_callback, ping)
+
+ rnr = ExperimentRunner()
+ runs = rnr.run(ec, min_runs = 5,
+ compute_metric_callback = metric_callback,
+ wait_guids = [ping],
+ wait_time = 0)
+
+ self.assertTrue(runs >= 5)
+
+ dircount = 0
+ for d in os.listdir(dirpath):
+ path = os.path.join(dirpath, d)
+ if os.path.isdir(path):
+ dircount += 1
+ logs = glob.glob(os.path.join(path, "*.stdout"))
+ self.assertEquals(len(logs), 1)
+
+ self.assertEquals(runs, dircount)
+
+ shutil.rmtree(dirpath)
+
+ def test_simple_multirun_fedora(self):
+ self.t_simple_multirun(self.fedora_host, self.fedora_user, "nc")
+
+ def test_simple_multirun_ubuntu(self):
+ self.t_simple_multirun(self.ubuntu_host, self.ubuntu_user, "netcat")
+
+if __name__ == '__main__':
+ unittest.main()
+
self.fedora_user = "inria_nepi"
self.fedora_identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'])
+ self.ubuntu_host = "roseval.pl.sophia.inria.fr"
+ self.ubuntu_user = "inria_nepi"
+ self.ubuntu_identity = "%s/.ssh/id_rsa" % (os.environ['HOME'])
+
@skipIfNotAlive
def t_dce_ping(self, host, user = None, identity = None):
ec = ExperimentController(exp_id = "test-dce-ping")
### create applications
ccnd1 = ec.register_resource("ns3::LinuxCCNDceApplication")
- # NOTE THAT INSTALLATION MIGHT FAIL IF openjdk-6-jdk is not installed
- ec.set(ccnd1, "depends", "libpcap0.8-dev openjdk-6-jdk ant1.8 autoconf "
- "libssl-dev libexpat-dev libpcap-dev libecryptfs0 libxml2-utils auto"
- "make gawk gcc g++ git-core pkg-config libpcre3-dev openjdk-6-jre-lib")
+ if host == self.fedora_host:
+ depends = ( " autoconf openssl-devel expat-devel libpcap-devel "
+ " ecryptfs-utils-devel libxml2-devel automake gawk "
+ " gcc gcc-c++ git pcre-devel make ")
+ else: # UBUNTU
+ # NOTE THAT INSTALLATION MIGHT FAIL IF openjdk-6-jdk is not installed
+ depends = ( "libpcap0.8-dev openjdk-6-jdk ant1.8 autoconf "
+ "libssl-dev libexpat-dev libpcap-dev libecryptfs0 libxml2-utils auto"
+ "make gawk gcc g++ git-core pkg-config libpcre3-dev openjdk-6-jre-lib")
+
+ ec.set (ccnd1, "depends", depends)
ec.set (ccnd1, "sources", "http://www.ccnx.org/releases/ccnx-0.7.2.tar.gz")
ec.set (ccnd1, "build", "tar zxf ${SRC}/ccnx-0.7.2.tar.gz && "
"cd ccnx-0.7.2 && "
ec.shutdown()
def test_dce_ping_fedora(self):
- self.t_dce_ping(self.fedora_host, self.fedora_user, self.fedora_identity)
+ self.t_dce_ping(self.fedora_host, self.fedora_user, self.fedora_identity)
+
+ def test_dce_ping_ubuntu(self):
+ self.t_dce_ping(self.ubuntu_host, self.ubuntu_user, self.ubuntu_identity)
def test_dce_ping_local(self):
self.t_dce_ping("localhost")
def test_dce_ccn_fedora(self):
self.t_dce_ccn(self.fedora_host, self.fedora_user, self.fedora_identity)
+ def test_dce_ccn_ubuntu(self):
+ self.t_dce_ccn(self.ubuntu_host, self.ubuntu_user, self.ubuntu_identity)
+
def test_dce_ccn_local(self):
self.t_dce_ccn("localhost")
--- /dev/null
+#!/usr/bin/env python
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.execution.ec import ExperimentController
+from nepi.execution.trace import TraceAttr
+
+from test_utils import skipIfNotAlive
+
+import os
+import shutil
+import time
+import tempfile
+import unittest
+
+def add_ns3_node(ec, simu):
+ node = ec.register_resource("ns3::Node")
+ ec.register_connection(node, simu)
+
+ ipv4 = ec.register_resource("ns3::Ipv4L3Protocol")
+ ec.register_connection(node, ipv4)
+
+ arp = ec.register_resource("ns3::ArpL3Protocol")
+ ec.register_connection(node, arp)
+
+ icmp = ec.register_resource("ns3::Icmpv4L4Protocol")
+ ec.register_connection(node, icmp)
+
+ udp = ec.register_resource("ns3::UdpL4Protocol")
+ ec.register_connection(node, udp)
+
+ return node
+
+def add_point2point_device(ec, node, ip, prefix):
+ dev = ec.register_resource("ns3::PointToPointNetDevice")
+ ec.set(dev, "ip", ip)
+ ec.set(dev, "prefix", prefix)
+ ec.register_connection(node, dev)
+
+ queue = ec.register_resource("ns3::DropTailQueue")
+ ec.register_connection(dev, queue)
+
+ return dev
+
+def add_csma_device(ec, node, ip, prefix):
+ dev = ec.register_resource("ns3::CsmaNetDevice")
+ ec.set(dev, "ip", ip)
+ ec.set(dev, "prefix", prefix)
+ ec.register_connection(node, dev)
+
+ queue = ec.register_resource("ns3::DropTailQueue")
+ ec.register_connection(dev, queue)
+
+ return dev
+
+def add_wifi_device(ec, node, ip, prefix,
+ access_point = False):
+ dev = ec.register_resource("ns3::WifiNetDevice")
+ ec.set(dev, "ip", ip)
+ ec.set(dev, "prefix", prefix)
+ ec.register_connection(node, dev)
+
+ phy = ec.register_resource("ns3::YansWifiPhy")
+ ec.set(phy, "Standard", "WIFI_PHY_STANDARD_80211a")
+ ec.register_connection(dev, phy)
+
+ error = ec.register_resource("ns3::NistErrorRateModel")
+ ec.register_connection(phy, error)
+
+ manager = ec.register_resource("ns3::ArfWifiManager")
+ ec.register_connection(dev, manager)
+
+ if access_point:
+ mac = ec.register_resource("ns3::ApWifiMac")
+ else:
+ mac = ec.register_resource("ns3::StaWifiMac")
+
+ ec.set(mac, "Standard", "WIFI_PHY_STANDARD_80211a")
+ ec.register_connection(dev, mac)
+
+ return dev, phy
+
+def add_random_mobility(ec, node, x, y, z, speed, bounds_width,
+ bounds_height):
+ position = "%d:%d:%d" % (x, y, z)
+ bounds = "0|%d|0|%d" % (bounds_width, bounds_height)
+ speed = "ns3::UniformRandomVariable[Min=%d|Max=%s]" % (speed, speed)
+ pause = "ns3::ConstantRandomVariable[Constant=1.0]"
+
+ mobility = ec.register_resource("ns3::RandomDirection2dMobilityModel")
+ ec.set(mobility, "Position", position)
+ ec.set(mobility, "Bounds", bounds)
+ ec.set(mobility, "Speed", speed)
+ ec.set(mobility, "Pause", pause)
+ ec.register_connection(node, mobility)
+ return mobility
+
+def add_constant_mobility(ec, node, x, y, z):
+ mobility = ec.register_resource("ns3::ConstantPositionMobilityModel")
+ position = "%d:%d:%d" % (x, y, z)
+ ec.set(mobility, "Position", position)
+ ec.register_connection(node, mobility)
+ return mobility
+
+def add_wifi_channel(ec):
+ channel = ec.register_resource("ns3::YansWifiChannel")
+ delay = ec.register_resource("ns3::ConstantSpeedPropagationDelayModel")
+ ec.register_connection(channel, delay)
+
+ loss = ec.register_resource("ns3::LogDistancePropagationLossModel")
+ ec.register_connection(channel, loss)
+
+ return channel
+
+class LinuxNS3SimulationSerializationTest(unittest.TestCase):
+ def setUp(self):
+ #self.fedora_host = "nepi2.pl.sophia.inria.fr"
+ self.fedora_host = "planetlab1.informatik.uni-erlangen.de"
+ self.fedora_user = "inria_nepi"
+ self.fedora_identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'])
+
+ @skipIfNotAlive
+ def t_wifi_serialize(self, host, user = None, identity = None):
+ bounds_width = bounds_height = 200
+ x = y = 100
+ speed = 1
+
+ dirpath = tempfile.mkdtemp()
+
+ ec = ExperimentController(exp_id = "test-ns3-wifi-ping")
+
+ node = ec.register_resource("LinuxNode")
+ if host == "localhost":
+ ec.set(node, "hostname", "localhost")
+ else:
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "identity", identity)
+
+ ec.set(node, "cleanProcesses", True)
+ #ec.set(node, "cleanHome", True)
+
+ simu = ec.register_resource("LinuxNS3Simulation")
+ ec.set(simu, "verbose", True)
+ ec.register_connection(simu, node)
+
+ nsnode1 = add_ns3_node(ec, simu)
+ dev1, phy1 = add_wifi_device(ec, nsnode1, "10.0.0.1", "24", access_point = True)
+ mobility1 = add_constant_mobility(ec, nsnode1, x, y, 0)
+
+ nsnode2 = add_ns3_node(ec, simu)
+ dev2, phy2 = add_wifi_device(ec, nsnode2, "10.0.0.2", "24", access_point = False)
+ mobility1 = add_constant_mobility(ec, nsnode2, x, y, 0)
+ #mobility2 = add_random_mobility(ec, nsnode2, x, y, 0, speed, bounds_width, bounds_height)
+
+ # Create channel
+ chan = add_wifi_channel(ec)
+ ec.register_connection(chan, phy1)
+ ec.register_connection(chan, phy2)
+
+ ### create pinger
+ ping = ec.register_resource("ns3::V4Ping")
+ ec.set (ping, "Remote", "10.0.0.1")
+ ec.set (ping, "Interval", "1s")
+ ec.set (ping, "Verbose", True)
+ ec.set (ping, "StartTime", "1s")
+ ec.set (ping, "StopTime", "21s")
+ ec.register_connection(ping, nsnode2)
+
+ filepath = ec.save(dirpath)
+ print filepath
+
+ ec.deploy()
+
+ ec.wait_finished([ping])
+
+ stdout = ec.trace(simu, "stdout")
+
+ expected = "20 packets transmitted, 20 received, 0% packet loss"
+ self.assertTrue(stdout.find(expected) > -1)
+
+ ec.shutdown()
+
+ # Load serialized experiment
+ ec2 = ExperimentController.load(filepath)
+
+ ec2.deploy()
+
+ ec2.wait_finished([ping])
+
+ self.assertEquals(len(ec.resources), len(ec2.resources))
+
+ stdout = ec2.trace(simu, "stdout")
+
+ expected = "20 packets transmitted, 20 received, 0% packet loss"
+ self.assertTrue(stdout.find(expected) > -1)
+
+ ec2.shutdown()
+
+ shutil.rmtree(dirpath)
+
+ @skipIfNotAlive
+ def t_routing_serialize(self, host, user = None, identity = None):
+ """
+ network topology:
+ n4
+ |
+ n1 -- p2p -- n2 -- csma -- n5 -- p2p -- n6
+ | |
+ ping n6 n3
+
+
+ """
+ dirpath = tempfile.mkdtemp()
+
+ ec = ExperimentController(exp_id = "test-ns3-routes")
+
+ node = ec.register_resource("LinuxNode")
+ if host == "localhost":
+ ec.set(node, "hostname", host)
+ else:
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "identity", identity)
+
+ ec.set(node, "cleanProcesses", True)
+ #ec.set(node, "cleanHome", True)
+
+ simu = ec.register_resource("LinuxNS3Simulation")
+ ec.set(simu, "verbose", True)
+ ec.register_connection(simu, node)
+
+ nsnode1 = add_ns3_node(ec, simu)
+ p2p12 = add_point2point_device(ec, nsnode1, "10.0.0.1", "30")
+
+ nsnode2 = add_ns3_node(ec, simu)
+ p2p21 = add_point2point_device(ec, nsnode2, "10.0.0.2", "30")
+ csma2 = add_csma_device(ec, nsnode2, "10.0.1.1", "24")
+
+ nsnode3 = add_ns3_node(ec, simu)
+ csma3 = add_csma_device(ec, nsnode3, "10.0.1.2", "24")
+
+ nsnode4 = add_ns3_node(ec, simu)
+ csma4 = add_csma_device(ec, nsnode4, "10.0.1.3", "24")
+
+ nsnode5 = add_ns3_node(ec, simu)
+ p2p56 = add_point2point_device(ec, nsnode5, "10.0.2.1", "30")
+ csma5 = add_csma_device(ec, nsnode5, "10.0.1.4", "24")
+
+ nsnode6 = add_ns3_node(ec, simu)
+ p2p65 = add_point2point_device(ec, nsnode6, "10.0.2.2", "30")
+
+ # P2P chan1
+ p2p_chan1 = ec.register_resource("ns3::PointToPointChannel")
+ ec.set(p2p_chan1, "Delay", "0s")
+ ec.register_connection(p2p_chan1, p2p12)
+ ec.register_connection(p2p_chan1, p2p21)
+
+ # CSMA chan
+ csma_chan = ec.register_resource("ns3::CsmaChannel")
+ ec.set(csma_chan, "Delay", "0s")
+ ec.register_connection(csma_chan, csma2)
+ ec.register_connection(csma_chan, csma3)
+ ec.register_connection(csma_chan, csma4)
+ ec.register_connection(csma_chan, csma5)
+
+ # P2P chan2
+ p2p_chan2 = ec.register_resource("ns3::PointToPointChannel")
+ ec.set(p2p_chan2, "Delay", "0s")
+ ec.register_connection(p2p_chan2, p2p56)
+ ec.register_connection(p2p_chan2, p2p65)
+
+ # Add routes - n1 - n6
+ r1 = ec.register_resource("ns3::Route")
+ ec.set(r1, "network", "10.0.2.0")
+ ec.set(r1, "prefix", "30")
+ ec.set(r1, "nexthop", "10.0.0.2")
+ ec.register_connection(r1, nsnode1)
+
+ # Add routes - n2 - n6
+ r2 = ec.register_resource("ns3::Route")
+ ec.set(r2, "network", "10.0.2.0")
+ ec.set(r2, "prefix", "30")
+ ec.set(r2, "nexthop", "10.0.1.4")
+ ec.register_connection(r2, nsnode2)
+
+ # Add routes - n5 - n1
+ r5 = ec.register_resource("ns3::Route")
+ ec.set(r5, "network", "10.0.0.0")
+ ec.set(r5, "prefix", "30")
+ ec.set(r5, "nexthop", "10.0.1.1")
+ ec.register_connection(r5, nsnode5)
+
+ # Add routes - n6 - n1
+ r6 = ec.register_resource("ns3::Route")
+ ec.set(r6, "network", "10.0.0.0")
+ ec.set(r6, "prefix", "30")
+ ec.set(r6, "nexthop", "10.0.2.1")
+ ec.register_connection(r6, nsnode6)
+
+ ### create pinger
+ ping = ec.register_resource("ns3::V4Ping")
+ ec.set (ping, "Remote", "10.0.2.2")
+ ec.set (ping, "Interval", "1s")
+ ec.set (ping, "Verbose", True)
+ ec.set (ping, "StartTime", "1s")
+ ec.set (ping, "StopTime", "21s")
+ ec.register_connection(ping, nsnode1)
+
+ filepath = ec.save(dirpath)
+ print filepath
+
+ ec.deploy()
+
+ ec.wait_finished([ping])
+
+ stdout = ec.trace(simu, "stdout")
+
+ expected = "20 packets transmitted, 20 received, 0% packet loss"
+ self.assertTrue(stdout.find(expected) > -1)
+
+ ec.shutdown()
+
+ # Load serialized experiment
+ ec2 = ExperimentController.load(filepath)
+
+ ec2.deploy()
+
+ ec2.wait_finished([ping])
+
+ self.assertEquals(len(ec.resources), len(ec2.resources))
+
+ stdout = ec2.trace(simu, "stdout")
+
+ expected = "20 packets transmitted, 20 received, 0% packet loss"
+ self.assertTrue(stdout.find(expected) > -1)
+
+ ec2.shutdown()
+
+ shutil.rmtree(dirpath)
+
+ @skipIfNotAlive
+ def t_dce_serialize(self, host, user = None, identity = None):
+ dirpath = tempfile.mkdtemp()
+
+ ec = ExperimentController(exp_id = "test-ns3-dce")
+
+ node = ec.register_resource("LinuxNode")
+ if host == "localhost":
+ ec.set(node, "hostname", host)
+ else:
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "identity", identity)
+
+ ec.set(node, "cleanProcesses", True)
+ #ec.set(node, "cleanHome", True)
+
+ simu = ec.register_resource("LinuxNS3Simulation")
+ ec.set(simu, "verbose", True)
+ ec.register_connection(simu, node)
+
+ nsnode1 = add_ns3_node(ec, simu)
+ p2p1 = add_point2point_device(ec, nsnode1, "10.0.0.1", "30")
+ ec.set(p2p1, "DataRate", "5Mbps")
+
+ nsnode2 = add_ns3_node(ec, simu)
+ p2p2 = add_point2point_device(ec, nsnode2, "10.0.0.2", "30")
+ ec.set(p2p2, "DataRate", "5Mbps")
+
+ # Create channel
+ chan = ec.register_resource("ns3::PointToPointChannel")
+ ec.set(chan, "Delay", "2ms")
+
+ ec.register_connection(chan, p2p1)
+ ec.register_connection(chan, p2p2)
+
+ ### create applications
+ udp_perf = ec.register_resource("ns3::DceApplication")
+ ec.set (udp_perf, "binary", "udp-perf")
+ ec.set (udp_perf, "stackSize", 1<<20)
+ ec.set (udp_perf, "arguments", "--duration=10;--nodes=2")
+ ec.set (udp_perf, "StartTime", "1s")
+ ec.set (udp_perf, "StopTime", "20s")
+ ec.register_connection(udp_perf, nsnode1)
+
+ udp_perf_client = ec.register_resource("ns3::DceApplication")
+ ec.set (udp_perf_client, "binary", "udp-perf")
+ ec.set (udp_perf_client, "stackSize", 1<<20)
+ ec.set (udp_perf_client, "arguments", "--client;--nodes=2;--host=10.0.0.1;--duration=10")
+ ec.set (udp_perf_client, "StartTime", "2s")
+ ec.set (udp_perf_client, "StopTime", "20s")
+ ec.register_connection(udp_perf_client, nsnode2)
+
+ filepath = ec.save(dirpath)
+
+ ec.deploy()
+
+ ec.wait_finished([udp_perf_client])
+
+ # Give time to flush the streams
+ import time
+ time.sleep(5)
+
+ expected = "udp-perf --duration=10 --nodes=2"
+ cmdline = ec.trace(udp_perf, "cmdline")
+ self.assertTrue(cmdline.find(expected) > -1, cmdline)
+
+ expected = "Start Time: NS3 Time: 1s ("
+ status = ec.trace(udp_perf, "status")
+ self.assertTrue(status.find(expected) > -1, status)
+
+ expected = "received=1500 bytes, 1 reads (@1500 bytes) 1500"
+ stdout = ec.trace(udp_perf, "stdout")
+ self.assertTrue(stdout.find(expected) > -1, stdout)
+
+ ec.shutdown()
+
+ # Load serialized experiment
+ ec2 = ExperimentController.load(filepath)
+
+ ec2.deploy()
+ ec2.wait_finished([udp_perf_client])
+
+ # Give time to flush the streams
+ time.sleep(5)
+
+ self.assertEquals(len(ec.resources), len(ec2.resources))
+
+ expected = "udp-perf --duration=10 --nodes=2"
+ cmdline = ec2.trace(udp_perf, "cmdline")
+ self.assertTrue(cmdline.find(expected) > -1, cmdline)
+
+ expected = "Start Time: NS3 Time: 1s ("
+ status = ec2.trace(udp_perf, "status")
+ self.assertTrue(status.find(expected) > -1, status)
+
+ expected = "received=1500 bytes, 1 reads (@1500 bytes) 1500"
+ stdout = ec2.trace(udp_perf, "stdout")
+ self.assertTrue(stdout.find(expected) > -1, stdout)
+
+ ec2.shutdown()
+
+ shutil.rmtree(dirpath)
+
+ def test_wifi_serialize_fedora(self):
+ self.t_wifi_serialize(self.fedora_host, self.fedora_user, self.fedora_identity)
+
+ def test_wifi_serialize_local(self):
+ self.t_wifi_serialize("localhost")
+
+ def test_routing_serialize_fedora(self):
+ self.t_routing_serialize(self.fedora_host, self.fedora_user, self.fedora_identity)
+
+ def test_routing_serialize_local(self):
+ self.t_routing_serialize("localhost")
+
+ def test_dce_serialize_fedora(self):
+ self.t_dce_serialize(self.fedora_host, self.fedora_user, self.fedora_identity)
+
+ def test_dce_serialize_local(self):
+ self.t_dce_serialize("localhost")
+
+
+if __name__ == '__main__':
+ unittest.main()
+
--- /dev/null
+#!/usr/bin/env python
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.execution.ec import ExperimentController
+from nepi.execution.resource import ResourceState, ResourceAction
+from nepi.execution.trace import TraceAttr
+
+from test_utils import skipIfNotAlive, skipInteractive
+
+import os
+import shutil
+import time
+import tempfile
+import unittest
+
+class LinuxSerializationTestCase(unittest.TestCase):
+ def setUp(self):
+ self.fedora_host = "nepi2.pl.sophia.inria.fr"
+ self.fedora_user = "inria_nepi"
+
+ self.ubuntu_host = "roseval.pl.sophia.inria.fr"
+ self.ubuntu_user = "inria_nepi"
+
+ self.target = "nepi5.pl.sophia.inria.fr"
+
+ @skipIfNotAlive
+ def t_condition_serialize(self, host, user, depends):
+
+ dirpath = tempfile.mkdtemp()
+
+ ec = ExperimentController(exp_id="test-condition-serial")
+
+ node = ec.register_resource("LinuxNode")
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "cleanHome", True)
+ ec.set(node, "cleanProcesses", True)
+
+ server = ec.register_resource("LinuxApplication")
+ cmd = "echo 'HOLA' | nc -l 3333"
+ ec.set(server, "command", cmd)
+ ec.set(server, "depends", depends)
+ ec.register_connection(server, node)
+
+ client = ec.register_resource("LinuxApplication")
+ cmd = "nc 127.0.0.1 3333"
+ ec.set(client, "command", cmd)
+ ec.register_connection(client, node)
+
+ ec.register_condition(client, ResourceAction.START, server, ResourceState.STARTED)
+
+ apps = [client, server]
+
+ filepath = ec.save(dirpath)
+
+ ec.deploy()
+
+ ec.wait_finished(apps)
+
+ self.assertTrue(ec.state(node) == ResourceState.STARTED)
+ self.assertTrue(ec.state(server) == ResourceState.STOPPED)
+ self.assertTrue(ec.state(client) == ResourceState.STOPPED)
+
+ stdout = ec.trace(client, "stdout")
+ self.assertTrue(stdout.strip() == "HOLA")
+
+ ec.shutdown()
+
+ # Load serialized experiment
+ ec2 = ExperimentController.load(filepath)
+
+ ec2.deploy()
+ ec2.wait_finished(apps)
+
+ self.assertEquals(len(ec.resources), len(ec2.resources))
+
+ self.assertTrue(ec2.state(node) == ResourceState.STARTED)
+ self.assertTrue(ec2.state(server) == ResourceState.STOPPED)
+ self.assertTrue(ec2.state(client) == ResourceState.STOPPED)
+
+ stdout = ec2.trace(client, "stdout")
+
+ self.assertTrue(stdout.strip() == "HOLA")
+
+ ec2.shutdown()
+
+ shutil.rmtree(dirpath)
+
+ def test_condition_serialize_fedora(self):
+ self.t_condition_serialize(self.fedora_host, self.fedora_user, "nc")
+
+ def test_condition_serialize_ubuntu(self):
+ self.t_condition_serialize(self.ubuntu_host, self.ubuntu_user, "netcat")
+
+if __name__ == '__main__':
+ unittest.main()
+
+++ /dev/null
-#!/usr/bin/env python
-#
-# 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 <http://www.gnu.org/licenses/>.
-#
-# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
-
-
-from nepi.design.box import Box
-from nepi.util.parser import XMLParser
-
-import unittest
-
-class BoxDesignTestCase(unittest.TestCase):
- def test_to_xml(self):
- node1 = Box()
- node2 = Box()
-
- node1.label = "node1"
- node2.label = "node2"
-
- node1.connect(node2)
-
- node1.a.dog = "cat"
- node1.a.one = "two"
- node1.a.t = "q"
-
- node1.c.node2.a.sky = "sea"
- node2.a.bee = "honey"
-
- node1.tadd("unooo")
- node2.tadd("dosss")
-
- parser = XMLParser()
- xml = parser.to_xml(node1)
-
- node = parser.from_xml(xml)
- xml2 = parser.to_xml(node)
-
- self.assertEquals(xml, xml2)
-
-if __name__ == '__main__':
- unittest.main()
-
+++ /dev/null
-#!/usr/bin/env python
-#
-# 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 <http://www.gnu.org/licenses/>.
-#
-# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
-
-
-from nepi.design.box import Box
-from nepi.util.plot import Plotter
-
-import subprocess
-import unittest
-
-class BoxPlotTestCase(unittest.TestCase):
- def xtest_plot(self):
- """ XXX: This test is interactive, it will open an evince instance,
- so it should not run automatically """
- node1 = Box(label="node1")
- ping1 = Box(label="ping")
- mobility1 = Box(label="mob1")
- node2 = Box(label="node2")
- mobility2 = Box(label="mob2")
- iface1 = Box(label="iface1")
- iface2 = Box(label="iface2")
- channel = Box(label="chan")
-
- node1.connect(ping1)
- node1.connect(mobility1)
- node1.connect(iface1)
- channel.connect(iface1)
- channel.connect(iface2)
- node2.connect(iface2)
- node2.connect(mobility2)
-
- plotter = Plotter(node1)
- fname = plotter.plot()
- subprocess.call(["dot", "-Tps", fname, "-o", "%s.ps"%fname])
- subprocess.call(["evince","%s.ps"%fname])
-
-if __name__ == '__main__':
- unittest.main()
-
--- /dev/null
+#!/usr/bin/env python
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.execution.ec import ExperimentController
+from nepi.execution.resource import ResourceManager, ResourceState, \
+ clsinit_copy, ResourceAction, ResourceFactory
+from nepi.util.plotter import PFormats
+
+import os
+import tempfile
+import time
+import unittest
+
+reschedule_delay = "0.5s"
+deploy_time = 0
+run_time = 0
+
+class Link(ResourceManager):
+ _rtype = "dummy::Link"
+ def do_deploy(self):
+ time.sleep(deploy_time)
+ super(Link, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+class Interface(ResourceManager):
+ _rtype = "dummy::Interface"
+
+ def do_deploy(self):
+ node = self.get_connected(Node.get_rtype())[0]
+ link = self.get_connected(Link.get_rtype())[0]
+
+ if node.state < ResourceState.READY or \
+ link.state < ResourceState.READY:
+ self.ec.schedule(reschedule_delay, self.deploy)
+ self.logger.debug(" -------- RESCHEDULING ------- ")
+ else:
+ time.sleep(deploy_time)
+ super(Interface, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+class Node(ResourceManager):
+ _rtype = "dummy::Node"
+
+ def do_deploy(self):
+ self.logger.debug(" -------- DO_DEPLOY ------- ")
+ time.sleep(deploy_time)
+ super(Node, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+class Application(ResourceManager):
+ _rtype = "dummy::Application"
+
+ def do_deploy(self):
+ node = self.get_connected(Node.get_rtype())[0]
+
+ if node.state < ResourceState.READY:
+ self.ec.schedule(reschedule_delay, self.deploy)
+ self.logger.debug(" -------- RESCHEDULING ------- ")
+ else:
+ time.sleep(deploy_time)
+ super(Application, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+ def do_start(self):
+ super(Application, self).do_start()
+ time.sleep(run_time)
+ self.ec.schedule("0s", self.stop)
+
+ResourceFactory.register_type(Application)
+ResourceFactory.register_type(Node)
+ResourceFactory.register_type(Interface)
+ResourceFactory.register_type(Link)
+
+class PlotterTestCase(unittest.TestCase):
+ def test_serialize(self):
+ node_count = 4
+ app_count = 2
+
+ ec = ExperimentController(exp_id = "plotter-test")
+
+ # Add simulated nodes and applications
+ nodes = list()
+ apps = list()
+ ifaces = list()
+
+ for i in xrange(node_count):
+ node = ec.register_resource("dummy::Node")
+ nodes.append(node)
+
+ iface = ec.register_resource("dummy::Interface")
+ ec.register_connection(node, iface)
+ ifaces.append(iface)
+
+ for i in xrange(app_count):
+ app = ec.register_resource("dummy::Application")
+ ec.register_connection(node, app)
+ apps.append(app)
+
+ link = ec.register_resource("dummy::Link")
+
+ for iface in ifaces:
+ ec.register_connection(link, iface)
+
+ fpath = ec.plot(persist = True)
+ statinfo = os.stat(fpath)
+ size = statinfo.st_size
+ self.assertTrue(size > 0)
+ self.assertTrue(fpath.endswith(".png"))
+
+ fpath = ec.plot(persist = True, format = PFormats.DOT)
+ statinfo = os.stat(fpath)
+ size = statinfo.st_size
+ self.assertTrue(size > 0)
+ self.assertTrue(fpath.endswith(".dot"))
+
+ print fpath
+
+
+if __name__ == '__main__':
+ unittest.main()
+
--- /dev/null
+#!/usr/bin/env python
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.execution.ec import ExperimentController
+from nepi.execution.resource import ResourceManager, ResourceState, \
+ clsinit_copy, ResourceAction, ResourceFactory
+
+import os
+import tempfile
+import time
+import shutil
+import unittest
+
+reschedule_delay = "0.5s"
+deploy_time = 0
+run_time = 0
+
+class Link(ResourceManager):
+ _rtype = "dummy::Link"
+ def do_deploy(self):
+ time.sleep(deploy_time)
+ super(Link, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+class Interface(ResourceManager):
+ _rtype = "dummy::Interface"
+
+ def do_deploy(self):
+ node = self.get_connected(Node.get_rtype())[0]
+ link = self.get_connected(Link.get_rtype())[0]
+
+ if node.state < ResourceState.READY or \
+ link.state < ResourceState.READY:
+ self.ec.schedule(reschedule_delay, self.deploy)
+ self.logger.debug(" -------- RESCHEDULING ------- ")
+ else:
+ time.sleep(deploy_time)
+ super(Interface, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+class Node(ResourceManager):
+ _rtype = "dummy::Node"
+
+ def do_deploy(self):
+ self.logger.debug(" -------- DO_DEPLOY ------- ")
+ time.sleep(deploy_time)
+ super(Node, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+class Application(ResourceManager):
+ _rtype = "dummy::Application"
+
+ def do_deploy(self):
+ node = self.get_connected(Node.get_rtype())[0]
+
+ if node.state < ResourceState.READY:
+ self.ec.schedule(reschedule_delay, self.deploy)
+ self.logger.debug(" -------- RESCHEDULING ------- ")
+ else:
+ time.sleep(deploy_time)
+ super(Application, self).do_deploy()
+ self.logger.debug(" -------- DEPLOYED ------- ")
+
+ def do_start(self):
+ super(Application, self).do_start()
+ time.sleep(run_time)
+ self.ec.schedule("0s", self.stop)
+
+ResourceFactory.register_type(Application)
+ResourceFactory.register_type(Node)
+ResourceFactory.register_type(Interface)
+ResourceFactory.register_type(Link)
+
+class SerializerTestCase(unittest.TestCase):
+ def test_serialize(self):
+ node_count = 4
+ app_count = 2
+
+ dirpath = tempfile.mkdtemp()
+
+ ec = ExperimentController(exp_id = "serialize-test")
+
+ # Add simulated nodes and applications
+ nodes = list()
+ apps = list()
+ ifaces = list()
+
+ for i in xrange(node_count):
+ node = ec.register_resource("dummy::Node")
+ nodes.append(node)
+
+ iface = ec.register_resource("dummy::Interface")
+ ec.register_connection(node, iface)
+ ifaces.append(iface)
+
+ for i in xrange(app_count):
+ app = ec.register_resource("dummy::Application")
+ ec.register_connection(node, app)
+ apps.append(app)
+
+ link = ec.register_resource("dummy::Link")
+
+ for iface in ifaces:
+ ec.register_connection(link, iface)
+
+ filepath = ec.save(dirpath)
+
+ ec.deploy()
+
+ # Wait until nodes and apps are deployed
+ ec.wait_finished(apps)
+
+ # Do the experiment controller shutdown
+ ec.shutdown()
+
+ # Load serialized experiment
+ ec2 = ExperimentController.load(filepath)
+ apps = ec2.get_resources_by_type("dummy::Application")
+ ec2.deploy()
+ ec2.wait_finished(apps)
+ ec2.shutdown()
+
+ self.assertEquals(len(ec.resources), len(ec2.resources))
+
+ shutil.rmtree(dirpath)
+
+if __name__ == '__main__':
+ unittest.main()
+