From: Alina Quereilhac Date: Wed, 26 Nov 2014 22:49:38 +0000 (+0100) Subject: Adding dummy ns3 fd-net-device test X-Git-Tag: nepi-3.2.0~54 X-Git-Url: http://git.onelab.eu/?p=nepi.git;a=commitdiff_plain;h=5070e9681cddf7543189639d5fb4fcd76f7396e2 Adding dummy ns3 fd-net-device test --- diff --git a/src/nepi/resources/ns3/classes/lr_wpan_net_device.py b/src/nepi/resources/ns3/classes/lr_wpan_net_device.py new file mode 100644 index 00000000..0a74fffe --- /dev/null +++ b/src/nepi/resources/ns3/classes/lr_wpan_net_device.py @@ -0,0 +1,50 @@ +# +# 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 . +# + +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 diff --git a/src/nepi/resources/ns3/ns3fdnetdevice.py b/src/nepi/resources/ns3/ns3fdnetdevice.py new file mode 100644 index 00000000..3e42d23c --- /dev/null +++ b/src/nepi/resources/ns3/ns3fdnetdevice.py @@ -0,0 +1,42 @@ +# +# 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 . +# +# Author: Alina Quereilhac + +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) + diff --git a/src/nepi/resources/ns3/ns3pipechanel.py b/src/nepi/resources/ns3/ns3pipechanel.py new file mode 100644 index 00000000..c74b24d6 --- /dev/null +++ b/src/nepi/resources/ns3/ns3pipechanel.py @@ -0,0 +1,89 @@ +# +# 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 . +# +# Author: Alina Quereilhac + +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) + diff --git a/src/nepi/resources/ns3/ns3wrapper.py b/src/nepi/resources/ns3/ns3wrapper.py index ebd0d67c..eadd71fa 100644 --- a/src/nepi/resources/ns3/ns3wrapper.py +++ b/src/nepi/resources/ns3/ns3wrapper.py @@ -262,51 +262,60 @@ class NS3Wrapper(object): 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) " % ( @@ -649,5 +658,28 @@ class NS3Wrapper(object): 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 diff --git a/test/resources/linux/ns3/ns3fdnetdevice.py b/test/resources/linux/ns3/ns3fdnetdevice.py new file mode 100755 index 00000000..326ac458 --- /dev/null +++ b/test/resources/linux/ns3/ns3fdnetdevice.py @@ -0,0 +1,127 @@ +#!/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 . +# +# Author: Alina Quereilhac + +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() +