--- /dev/null
+#
+# NEPI, a framework to manage network experiments
+# Copyright (C) 2014 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/>.
+#
+
+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
+
+@clsinit_copy
+class NS3LrWpanNetDevice(NS3BaseNetDevice):
+ _rtype = "ns3::LrWpanNetDevice"
+
+ @classmethod
+ def _register_attributes(cls):
+
+ attr_useacks = Attribute("UseAcks",
+ "Request acknowledgments for data frames.",
+ type = Types.Bool,
+ default = "True",
+ allowed = None,
+ range = None,
+ flags = Flags.Reserved | Flags.Construct)
+
+ cls._register_attribute(attr_useacks)
+
+
+
+ @classmethod
+ def _register_traces(cls):
+ pass
+
+ def __init__(self, ec, guid):
+ super(NS3LrWpanNetDevice, self).__init__(ec, guid)
+ self._home = "ns3-lr-wpan-net-device-%s" % self.guid
--- /dev/null
+#
+# NEPI, a framework to manage network experiments
+# Copyright (C) 2014 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.resource import clsinit_copy
+from nepi.resources.ns3.ns3netdevice import NS3BaseNetDevice
+
+@clsinit_copy
+class NS3BaseFdNetDevice(NS3BaseNetDevice):
+ _rtype = "abstract::ns3::FdNetDevice"
+
+ @property
+ def _rms_to_wait(self):
+ rms = set([self.node, self.node.ipv4])
+ return rms
+
+ def _configure_mac_address(self):
+ # The wifimac is the one responsible for
+ # configuring the MAC address
+ pass
+
+ def _connect_object(self):
+ node = self.node
+ if node and node.uuid not in self.connected:
+ self.simulation.invoke(node.uuid, "AddDevice", self.uuid)
+ self._connected.add(node.uuid)
+
--- /dev/null
+#
+# NEPI, a framework to manage network experiments
+# Copyright (C) 2014 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.attribute import Attribute, Flags, Types
+from nepi.execution.resource import clsinit_copy
+from nepi.resources.ns3.ns3base import NS3Base
+
+import socket
+
+@clsinit_copy
+class NS3BasePipeChannel(NS3Base):
+ """ Interconnects two FdNetDevices with a PIPE
+ """
+ _rtype = "ns3::PipeChannel"
+
+ def __init__(self, ec, guid):
+ super(NS3BasePipeChannel, self).__init__(ec, guid)
+ self._devices = None
+
+ @property
+ def devices(self):
+ if not self._devices:
+ from nepi.resources.ns3.ns3fdnetdevice import NS3BaseFdNetDevice
+ devices = self.get_connected(NS3BaseFdNetDevice.get_rtype())
+ if not devices or len(devices) != 2:
+ msg = "PipeChannel must be connected to exactly to two FdNetDevices"
+ self.error(msg)
+ raise RuntimeError, msg
+
+ self._devices = devices
+
+ return self._devices
+
+ @property
+ def node(self):
+ return self.devices[0].node
+
+ @property
+ def _rms_to_wait(self):
+ rms = set(self.devices)
+ return rms
+
+ def _instantiate_object(self):
+ """ The pipe channel does not really exists as an ns-3 object.
+ Do nothing, just validate that the simulator is in real time
+ mode, otherwise it is not going to work.
+ """
+ mode = self.simulator.get("simulatorImplementationType")
+ if mode != "ns3::RealtimeSimulatorImpl":
+ msg = "The simulation must run in real time!!"
+ self.error(msg)
+ raise RuntimeError, msg
+
+ def _connect_object(self):
+ dev1 = self.devices[0]
+ dev2 = self.devices[1]
+
+ if dev1.uuid not in self.connected and dev2.uuid not in self.connected:
+ (s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ self._send_fd(dev1, s0)
+ self._send_fd(dev2, s1)
+
+ def _send_fd(self, dev, fd):
+ import passfd
+
+ address = self.simulation.invoke(dev.uuid, "recvFD")
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ sock.connect(address)
+ passfd.sendfd(sock, fd, '0')
+
+ self._connected.add(dev.uuid)
+ dev._connected.add(self.uuid)
+
if operation == "isRunning":
result = self.is_running
+
elif operation == "isFinished":
result = self.is_finished
+
elif operation == "isAppRunning":
result = self._is_app_running(uuid)
- else:
- if operation == "addStaticRoute":
- result = self._add_static_route(uuid, *args)
-
- ### DUMP
- self.debuger.dump_invoke(result, uuid, operation, args, kwargs)
+ elif operation == "recvFD":
+ ### passFD operation binds to a different random socket
+ ### en every execution, so the socket name that could be
+ ### dumped to the debug script using dump_invoke is
+ ### not be valid accross debug executions.
+ result = self._recv_fd(uuid, *args, **kwargs)
- elif operation == "retrieveObject":
- result = self._retrieve_object(uuid, *args, **kwargs)
-
- ### DUMP
- self.debuger.dump_invoke(result, uuid, operation, args, kwargs)
+ elif operation == "addStaticRoute":
+ result = self._add_static_route(uuid, *args)
+
+ ### DUMP - result is static, so will be dumped as plain text
+ self.debuger.dump_invoke(result, uuid, operation, args, kwargs)
- else:
- newuuid = self.make_uuid()
+ elif operation == "retrieveObject":
+ result = self._retrieve_object(uuid, *args, **kwargs)
+
+ ### DUMP - result is static, so will be dumped as plain text
+ self.debuger.dump_invoke(result, uuid, operation, args, kwargs)
+
+ else:
+ newuuid = self.make_uuid()
- ### DUMP
- self.debuger.dump_invoke(newuuid, uuid, operation, args, kwargs)
+ ### DUMP - result is a uuid that encoded an dynamically generated
+ ### object
+ self.debuger.dump_invoke(newuuid, uuid, operation, args, kwargs)
- if uuid.startswith(SINGLETON):
- obj = self._singleton(uuid)
- else:
- obj = self.get_object(uuid)
-
- method = getattr(obj, operation)
+ if uuid.startswith(SINGLETON):
+ obj = self._singleton(uuid)
+ else:
+ obj = self.get_object(uuid)
+
+ method = getattr(obj, operation)
- # arguments starting with 'uuid' identify ns-3 C++
- # objects and must be replaced by the actual object
- realargs = self.replace_args(args)
- realkwargs = self.replace_kwargs(kwargs)
+ # arguments starting with 'uuid' identify ns-3 C++
+ # objects and must be replaced by the actual object
+ realargs = self.replace_args(args)
+ realkwargs = self.replace_kwargs(kwargs)
- result = method(*realargs, **realkwargs)
+ result = method(*realargs, **realkwargs)
- # If the result is an object (not a base value),
- # then keep track of the object a return the object
- # reference (newuuid)
- if not (result is None or type(result) in [
- bool, float, long, str, int]):
- self._objects[newuuid] = result
- result = newuuid
+ # If the result is an object (not a base value),
+ # then keep track of the object a return the object
+ # reference (newuuid)
+ if not (result is None or type(result) in [
+ bool, float, long, str, int]):
+ self._objects[newuuid] = result
+ result = newuuid
### DEBUG
self.logger.debug("RET INVOKE %s%s = %s -> %s(%s, %s) " % (
return newuuid
+ def _recv_fd(self, uuid):
+ """ Waits on a local address to receive a file descriptor
+ from a local process. The file descriptor is associated
+ to a FdNetDevice to stablish communication between the
+ simulation and what ever process writes on that file descriptor
+ """
+
+ def recvfd(sock, fdnd):
+ (fd, msg) = passfd.recvfd(sock)
+ # Store a reference to the endpoint to keep the socket alive
+ fdnd.SetFileDescriptor(fd)
+
+ import passfd
+ import socket
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ sock.bind("")
+ address = sock.getsockname()
+
+ fdnd = self.get_object(uuid)
+ t = threading.Thread(target=recvfd, args=(sock,fdnd))
+ t.start()
+
+ return address
--- /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 time
+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_fd_device(ec, node, ip, prefix):
+ dev = ec.register_resource("ns3::FdNetDevice")
+ 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
+
+class LinuxNS3FdNetDeviceTest(unittest.TestCase):
+ def setUp(self):
+ self.fedora_host = "nepi2.pl.sophia.inria.fr"
+ self.fedora_user = "inria_nepi"
+ self.fedora_identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'])
+
+ @skipIfNotAlive
+ def t_dummy(self, host, user = None, identity = None):
+ ec = ExperimentController(exp_id = "test-ns3-fd-dummy")
+
+ 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, "cleanExperiment", True)
+
+ simu = ec.register_resource("LinuxNS3Simulation")
+ ec.set(simu, "simulatorImplementationType", "ns3::RealtimeSimulatorImpl")
+ ec.set(simu, "checksumEnabled", True)
+ ec.set(simu, "verbose", True)
+ ec.register_connection(simu, node)
+
+ nsnode1 = add_ns3_node(ec, simu)
+ dev1 = add_fd_device(ec, nsnode1, "10.0.0.1", "30")
+
+ nsnode2 = add_ns3_node(ec, simu)
+ dev2 = add_fd_device(ec, nsnode2, "10.0.0.2", "30")
+
+ channel = ec.register_resource("ns3::PipeChannel")
+ ec.register_connection(channel, dev1)
+ ec.register_connection(channel, dev2)
+
+ ### create pinger
+ ping = ec.register_resource("ns3::V4Ping")
+ ec.set (ping, "Remote", "10.0.0.2")
+ ec.set (ping, "Interval", "1s")
+ ec.set (ping, "Verbose", True)
+ ec.set (ping, "StartTime", "0s")
+ ec.set (ping, "StopTime", "20s")
+ ec.register_connection(ping, nsnode1)
+
+ ec.deploy()
+
+ ec.wait_finished([ping])
+
+ stdout = ec.trace(simu, "stdout")
+
+ print stdout
+
+ expected = "20 packets transmitted, 20 received, 0% packet loss"
+ self.assertTrue(stdout.find(expected) > -1)
+
+ ec.shutdown()
+
+ def ztest_dummy_fedora(self):
+ self.t_dummy(self.fedora_host, self.fedora_user, self.fedora_identity)
+
+ def test_dummy_local(self):
+ self.t_dummy("localhost")
+
+
+if __name__ == '__main__':
+ unittest.main()
+