From 76ec8fbc6c987d4208dd6d987de55a2ca543962f Mon Sep 17 00:00:00 2001 From: Alina Quereilhac Date: Fri, 16 Jan 2015 02:09:26 +0100 Subject: [PATCH] Linux/Ns-3/Dce cross experiments --- setup.py | 1 + src/nepi/resources/linux/node.py | 62 +++---- src/nepi/resources/linux/ns3/tap_fd_link.py | 1 - .../resources/linux/ns3/tun_tap_fd_link.py | 167 ++++++++++++++++++ .../resources/linux/scripts/linux-passfd.py | 61 +++++++ .../linux/scripts/linux-udp-connect.py | 44 ++--- src/nepi/resources/ns3/ns3fdnetdevice.py | 9 +- src/nepi/resources/planetlab/ns3/__init__.py | 0 .../planetlab/ns3/tun_tap_fd_link.py | 155 ++++++++++++++++ .../planetlab/scripts/pl-vif-passfd.py | 41 +++++ .../linux/ns3/cross_dce_linux_ccn.py | 164 +++++++++++++++++ .../linux/ns3/cross_ns3_linux_ping.py | 13 +- .../planetlab/ns3/cross_ns3_linux_ping.py | 159 +++++++++++++++++ 13 files changed, 809 insertions(+), 68 deletions(-) create mode 100644 src/nepi/resources/linux/ns3/tun_tap_fd_link.py create mode 100644 src/nepi/resources/linux/scripts/linux-passfd.py create mode 100644 src/nepi/resources/planetlab/ns3/__init__.py create mode 100644 src/nepi/resources/planetlab/ns3/tun_tap_fd_link.py create mode 100644 src/nepi/resources/planetlab/scripts/pl-vif-passfd.py create mode 100755 test/resources/linux/ns3/cross_dce_linux_ccn.py create mode 100755 test/resources/planetlab/ns3/cross_ns3_linux_ping.py diff --git a/setup.py b/setup.py index 01aba6dd..d269df50 100755 --- a/setup.py +++ b/setup.py @@ -26,6 +26,7 @@ setup( "nepi.resources.ns3.classes", "nepi.resources.omf", "nepi.resources.planetlab", + "nepi.resources.planetlab.ns3", "nepi.resources.planetlab.openvswitch", "nepi.util", "nepi.util.parsers", diff --git a/src/nepi/resources/linux/node.py b/src/nepi/resources/linux/node.py index e63a92e9..ff19bbb6 100644 --- a/src/nepi/resources/linux/node.py +++ b/src/nepi/resources/linux/node.py @@ -844,17 +844,18 @@ class LinuxNode(ResourceManager): return self.execute(cmd, with_lock = True) def run_and_wait(self, command, home, - shfile = "cmd.sh", - env = None, - overwrite = True, - pidfile = "pidfile", - ecodefile = "exitcode", - stdin = None, - stdout = "stdout", - stderr = "stderr", - sudo = False, - tty = False, - raise_on_error = True): + shfile="cmd.sh", + env=None, + overwrite=True, + wait_run=True, + pidfile="pidfile", + ecodefile="exitcode", + stdin=None, + stdout="stdout", + stderr="stderr", + sudo=False, + tty=False, + raise_on_error=True): """ Uploads the 'command' to a bash script in the host. Then runs the script detached in background in the host, and @@ -893,25 +894,26 @@ class LinuxNode(ResourceManager): pidfile = pidfile, raise_on_error = raise_on_error) - # wait until command finishes to execute - self.wait_run(pid, ppid) - - (eout, err), proc = self.check_errors(home, - ecodefile = ecodefile, - stderr = stderr) + if wait_run: + # wait until command finishes to execute + self.wait_run(pid, ppid) + + (eout, err), proc = self.check_errors(home, + ecodefile = ecodefile, + stderr = stderr) - # Out is what was written in the stderr file - if err: - msg = " Failed to run command '%s' " % command - self.error(msg, eout, err) + # Out is what was written in the stderr file + if err: + msg = " Failed to run command '%s' " % command + self.error(msg, eout, err) - if raise_on_error: - raise RuntimeError, msg + if raise_on_error: + raise RuntimeError, msg (out, oerr), proc = self.check_output(home, stdout) return (out, err), proc - + def exitcode(self, home, ecodefile = "exitcode"): """ Get the exit code of an application. @@ -935,10 +937,10 @@ class LinuxNode(ResourceManager): return ExitCode.ERROR def upload_command(self, command, - shfile = "cmd.sh", - ecodefile = "exitcode", - overwrite = True, - env = None): + shfile="cmd.sh", + ecodefile="exitcode", + overwrite=True, + env=None): """ Saves the command as a bash script file in the remote host, and forces to save the exit code of the command execution to the ecodefile """ @@ -958,9 +960,9 @@ class LinuxNode(ResourceManager): # Add environ to command command = environ + command - return self.upload(command, shfile, text = True, overwrite = overwrite) + return self.upload(command, shfile, text=True, overwrite=overwrite) - def format_environment(self, env, inline = False): + def format_environment(self, env, inline=False): """ Formats the environment variables for a command to be executed either as an inline command (i.e. export PYTHONPATH=src/..; export LALAL= ..;python script.py) or diff --git a/src/nepi/resources/linux/ns3/tap_fd_link.py b/src/nepi/resources/linux/ns3/tap_fd_link.py index 3f90ccb7..3740f6a3 100644 --- a/src/nepi/resources/linux/ns3/tap_fd_link.py +++ b/src/nepi/resources/linux/ns3/tap_fd_link.py @@ -117,4 +117,3 @@ class TapFdLink(ResourceManager): return fd - diff --git a/src/nepi/resources/linux/ns3/tun_tap_fd_link.py b/src/nepi/resources/linux/ns3/tun_tap_fd_link.py new file mode 100644 index 00000000..cf658fc3 --- /dev/null +++ b/src/nepi/resources/linux/ns3/tun_tap_fd_link.py @@ -0,0 +1,167 @@ +# +# 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 ResourceState, clsinit_copy, \ + ResourceManager +from nepi.resources.linux.application import LinuxApplication + +import base64 +import fcntl +import os +import socket +import struct + +@clsinit_copy +class LinuxTunTapFdLink(LinuxApplication): + """ Interconnects a TAP or TUN Linux device to a FdNetDevice + """ + _rtype = "linux::ns3::TunTapFdLink" + + def __init__(self, ec, guid): + super(LinuxTunTapFdLink, self).__init__(ec, guid) + self._tap = None + self._fdnetdevice = None + self._fd = None + self._fd_node = None + self.send_address = None + + @property + def fdnetdevice(self): + if not self._fdnetdevice: + from nepi.resources.ns3.ns3fdnetdevice import NS3BaseFdNetDevice + devices = self.get_connected(NS3BaseFdNetDevice.get_rtype()) + if not devices or len(devices) != 1: + msg = "linux::ns3::TunTapFdLink must be connected to exactly one FdNetDevice" + self.error(msg) + raise RuntimeError, msg + + self._fdnetdevice = devices[0] + + simu = self._fdnetdevice.simulation + from nepi.resources.linux.node import LinuxNode + nodes = simu.get_connected(LinuxNode.get_rtype()) + self._fd_node = nodes[0] + + return self._fdnetdevice + + @property + def fdnode(self): + return self._fd_node + + @property + def tap(self): + if not self._tap: + from nepi.resources.linux.tap import LinuxTap + devices = self.get_connected(LinuxTap.get_rtype()) + if not devices or len(devices) != 1: + msg = "linux::ns3::TunTapLink must be connected to exactly one LinuxTap" + self.error(msg) + raise RuntimeError, msg + + self._tap = devices[0] + + return self._tap + + @property + def tapnode(self): + return self.tap.node + + @property + def node(self): + return self.tapnode + + def upload_sources(self): + scripts = [] + + # vif-passfd python script + linux_passfd = os.path.join(os.path.dirname(__file__), + "..", + "scripts", + "linux-passfd.py") + + scripts.append(linux_passfd) + + # Upload scripts + scripts = ";".join(scripts) + + self.node.upload(scripts, + os.path.join(self.node.src_dir), + overwrite = False) + + def upload_start_command(self): + if self.tapnode.get("hostname") != \ + self.fdnode.get("hostname"): + msg = "Tap and FdNetDevice are not in the same host" + self.error(msg) + raise RuntimeError, msg + + self.send_address = self.fdnetdevice.recv_fd() + self.set("command", self._start_command) + + command = self.get("command") + + shfile = os.path.join(self.app_home, "start.sh") + self.node.run_and_wait(command, self.run_home, + shfile=shfile, + wait_run=False, + overwrite=True) + + def do_deploy(self): + if self.tap.state < ResourceState.READY or \ + self.fdnetdevice.state < ResourceState.READY: + self.ec.schedule(self.reschedule_delay, self.deploy) + else: + self.do_discover() + self.do_provision() + + self.set_ready() + + def do_start(self): + if self.state == ResourceState.READY: + command = self.get("command") + self.info("Starting command '%s'" % command) + + self.set_started() + else: + msg = " Failed to execute command '%s'" % command + self.error(msg, out, err) + raise RuntimeError, msg + + @property + def _start_command(self): + vif_name = self.ec.get(self.tap.guid, "deviceName") + vif_type = self.tap.vif_type_flag + pi = self.ec.get(self.tap.guid, "pi") + address = base64.b64encode(self.send_address) + + command = [] + # Use pl-vif-passfd.py to send fd from TAP to FdNetDevice + command.append("PYTHONPATH=$PYTHONPATH:${SRC}") + command.append("python ${SRC}/linux-passfd.py") + command.append("-a %s" % address) + command.append("-N %s " % vif_name) + command.append("-t %s " % vif_type) + if pi: + command.append("-p") + + command = " ".join(command) + command = self.replace_paths(command) + + return command + diff --git a/src/nepi/resources/linux/scripts/linux-passfd.py b/src/nepi/resources/linux/scripts/linux-passfd.py new file mode 100644 index 00000000..51cf4e87 --- /dev/null +++ b/src/nepi/resources/linux/scripts/linux-passfd.py @@ -0,0 +1,61 @@ +import base64 +import errno +import fcntl +import os +import passfd +import socket +import struct + +from optparse import OptionParser + +IFF_TUN = 0x0001 +IFF_TAP = 0x0002 +IFF_NO_PI = 0x1000 +TUNSETIFF = 0x400454ca + +def get_options(): + usage = ("usage: %prog -a
-N -t -p ") + + parser = OptionParser(usage = usage) + + parser.add_option("-a", "--address", dest="address", + help="Socket address to send file descriptor to", type="str") + parser.add_option("-N", "--vif-name", dest="vif_name", + help="The name of the virtual interface", type="str") + parser.add_option("-t", "--vif-type", dest="vif_type", + help="Virtual interface type. Either IFF_TAP or IFF_TUN. " + "Defaults to IFF_TAP. ", default=IFF_TAP, type="str") + parser.add_option("-p", "--pi", dest="pi", action="store_true", + default=False, help="Enable PI header") + + (options, args) = parser.parse_args() + + vif_type = IFF_TAP + if options.vif_type and options.vif_type == "IFF_TUN": + vif_type = IFF_TUN + + return (options.address, options.vif_name, vif_type, options.pi) + +if __name__ == '__main__': + + (address, vif_name, vif_type, pi) = get_options() + + flags = 0 + flags |= vif_type + + if not pi: + flags |= IFF_NO_PI + + fd = os.open("/dev/net/tun", os.O_RDWR) + + err = fcntl.ioctl(fd, TUNSETIFF, struct.pack("16sH", vif_name, flags)) + if err < 0: + os.close(fd) + raise RuntimeError("Could not retrive file descriptor from %s" % vif_name) + + address = base64.b64decode(address) + + sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + sock.connect(address) + passfd.sendfd(sock, fd, '0') + diff --git a/src/nepi/resources/linux/scripts/linux-udp-connect.py b/src/nepi/resources/linux/scripts/linux-udp-connect.py index ffd87400..330ed17f 100644 --- a/src/nepi/resources/linux/scripts/linux-udp-connect.py +++ b/src/nepi/resources/linux/scripts/linux-udp-connect.py @@ -60,51 +60,37 @@ def get_options(): parser = OptionParser(usage = usage) parser.add_option("-N", "--vif-name", dest="vif_name", - help = "The name of the virtual interface", - type="str") - + help="The name of the virtual interface", type="str") parser.add_option("-t", "--vif-type", dest="vif_type", - help = "Virtual interface type. Either IFF_TAP or IFF_TUN. " - "Defaults to IFF_TAP. ", - default = IFF_TAP, - type="str") - - parser.add_option("-n", "--pi", dest="pi", - action="store_true", - default = False, - help="Enable PI header") + help="Virtual interface type. Either IFF_TAP or IFF_TUN. " + "Defaults to IFF_TAP. ", default=IFF_TAP, type="str") + parser.add_option("-n", "--pi", dest="pi", action="store_true", + default=False, help="Enable PI header") parser.add_option("-b", "--bwlimit", dest="bwlimit", - help = "Specifies the interface's emulated bandwidth in bytes ", - default = None, type="int") - + help="Specifies the interface's emulated bandwidth in bytes ", + default=None, type="int") parser.add_option("-q", "--txqueuelen", dest="txqueuelen", - help = "Specifies the interface's transmission queue length. ", - default = 1000, type="int") - + help="Specifies the interface's transmission queue length. ", + default=1000, type="int") parser.add_option("-c", "--cipher", dest="cipher", - help = "Cipher to encript communication. " + help="Cipher to encript communication. " "One of PLAIN, AES, Blowfish, DES, DES3. ", - default = None, type="str") - + default=None, type="str") parser.add_option("-k", "--cipher-key", dest="cipher_key", - help = "Specify a symmetric encryption key with which to protect " + help="Specify a symmetric encryption key with which to protect " "packets across the tunnel. python-crypto must be installed " "on the system." , - default = None, type="str") + default=None, type="str") parser.add_option("-l", "--local-port-file", dest="local_port_file", help = "File where to store the local binded UDP port number ", default = "local_port_file", type="str") - parser.add_option("-r", "--remote-port-file", dest="remote_port_file", help = "File where to read the remote UDP port number to connect to", default = "remote_port_file", type="str") - parser.add_option("-H", "--remote-host", dest="remote_host", - help = "Remote host IP", - default = "remote_host", type="str") - + help = "Remote host IP", default = "remote_host", type="str") parser.add_option("-R", "--ret-file", dest="ret_file", help = "File where to store return code (success of connection) ", default = "ret_file", type="str") @@ -178,7 +164,7 @@ if __name__ == '__main__': # Establish tunnel tunchannel.tun_fwd(tun, remote, - with_pi = True, # Planetlab TAP devices add PI headers + with_pi = pi, # Planetlab TAP devices add PI headers ether_mode = (vif_type == IFF_TAP), udp = True, cipher_key = cipher_key, diff --git a/src/nepi/resources/ns3/ns3fdnetdevice.py b/src/nepi/resources/ns3/ns3fdnetdevice.py index b8aabe6a..02c11c63 100644 --- a/src/nepi/resources/ns3/ns3fdnetdevice.py +++ b/src/nepi/resources/ns3/ns3fdnetdevice.py @@ -53,12 +53,7 @@ class NS3BaseFdNetDevice(NS3BaseNetDevice): super(NS3BaseFdNetDevice, self)._instantiate_object() - def send_fd(self, fd): - import passfd - import socket - + def recv_fd(self): address = self.simulation.invoke(self.uuid, "recvFD") - sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) - sock.connect(address) - passfd.sendfd(sock, fd, '0') + return address diff --git a/src/nepi/resources/planetlab/ns3/__init__.py b/src/nepi/resources/planetlab/ns3/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/nepi/resources/planetlab/ns3/tun_tap_fd_link.py b/src/nepi/resources/planetlab/ns3/tun_tap_fd_link.py new file mode 100644 index 00000000..c658fb83 --- /dev/null +++ b/src/nepi/resources/planetlab/ns3/tun_tap_fd_link.py @@ -0,0 +1,155 @@ +# +# 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 ResourceManager, ResourceState, \ + clsinit_copy + +import os +import socket +import struct +import fcntl + +@clsinit_copy +class PlanetlabTunTapFdLink(ResourceManager): + """ Interconnects a TAP or TUN Linux device to a FdNetDevice + """ + _rtype = "planetlab::ns3::TunTapFdLink" + + def __init__(self, ec, guid): + super(PlanetlabTunTapFdLink, self).__init__(ec, guid) + self._tap = None + self._fdnetdevice = None + self._fd_sock_address = None + + @property + def fdnetdevice(self): + if not self._fdnetdevice: + from nepi.resources.ns3.ns3fdnetdevice import NS3BaseFdNetDevice + devices = self.get_connected(NS3BaseFdNetDevice.get_rtype()) + if not devices or len(devices) != 1: + msg = "TapFdLink must be connected to exactly one FdNetDevices" + self.error(msg) + raise RuntimeError, msg + + self._fdnetdevice = devices[0] + + return self._fdnetdevice + + @property + def tap(self): + if not self._tap: + from nepi.resources.planetlab.tap import PlanetlabTap + devices = self.get_connected(PlanetlabTap.get_rtype()) + + if not devices or len(devices) != 1: + msg = "TapFdLink must be connected to exactly one PlanetlabTap" + self.error(msg) + raise RuntimeError, msg + + self._tap = devices[0] + + return self._tap + + @property + def fd_sock_address(self): + return self._fd_sock_address + + @property + def node(self): + return self.tap.node + + def upload_sources(self): + scripts = [] + + # vif-passfd python script + pl_vif_passfd = os.path.join(os.path.dirname(__file__), "scripts", + "pl-vif-passfd.py") + + scripts.append(pl_vif_passfd) + + # Upload scripts + scripts = ";".join(scripts) + + self.node.upload(scripts, + os.path.join(self.node.src_dir), + overwrite = False) + + def upload_start_command(self): + if self.tap.node.get("hostname") != \ + self.fdnetdevice.node.get("hostname"): + msg = "Tap and FdNetDevice are not in the same host" + self.error(msg) + raise RuntimeError, msg + + self._fd_sock_address = self.fdnetdevice.recv_fd() + self.set("command", self._start_command) + + command = self.get("command") + env = self.get("env") + + # We want to make sure the ccnd is running + # before the experiment starts. + # Run the command as a bash script in background, + # in the host ( but wait until the command has + # finished to continue ) + env = self.replace_paths(env) + command = self.replace_paths(command) + + shfile = os.path.join(self.app_home, "start.sh") + self.node.run_and_wait(command, self.run_home, + shfile = shfile, + overwrite = True) + + def do_deploy(self): + if self.tap.state < ResourceState.READY or \ + self.fdnetdevice.state < ResourceState.READY: + self.ec.schedule(self.reschedule_delay, self.deploy) + else: + self.do_discover() + self.do_provision() + + super(PlanetlabTunTapFdLink, self).do_deploy() + + def do_start(self): + if self.state == ResourceState.READY: + command = self.get("command") + self.info("Starting command '%s'" % command) + + self.set_started() + else: + msg = " Failed to execute command '%s'" % command + self.error(msg, out, err) + raise RuntimeError, msg + + @property + def _start_command(self): + command = [] + # Use pl-vif-passfd.py to send fd from TAP to FdNetDevice + command.append("sudo -S") + command.append("PYTHONPATH=$PYTHONPATH:${SRC}") + command.append("python ${SRC}/pl-vif-passfd") + command.append("-a %s" % self.fd_sock_address) + command.append("-S %s " % self.tap.sock_name) + + command = " ".join(command) + command = self.replace_paths(command) + + return command + diff --git a/src/nepi/resources/planetlab/scripts/pl-vif-passfd.py b/src/nepi/resources/planetlab/scripts/pl-vif-passfd.py new file mode 100644 index 00000000..f1f3ce54 --- /dev/null +++ b/src/nepi/resources/planetlab/scripts/pl-vif-passfd.py @@ -0,0 +1,41 @@ +import base64 +import errno +import os +import passfd +import socket + +from optparse import OptionParser + +PASSFD_MSG = "PASSFD" + +def get_options(): + usage = ("usage: %prog -a
-S ") + + parser = OptionParser(usage = usage) + + parser.add_option("-a", "--address", dest="address", + help = "Socket address to send file descriptor to", type="str") + parser.add_option("-S", "--vif-socket", dest="vif_socket", + help = "Name for the unix socket to request the TAP file descriptor", + default = "tap.sock", type="str") + + (options, args) = parser.parse_args() + + return (options.address, options.fd_socket_name) + +if __name__ == '__main__': + + (address, socket_name) = get_options() + + # This script sends a message (PASSFD_MSG) to the process that created + # the TUN/TAP device to request that it sens the file descriptor associated + # to the TUN/TAP to another process. The other process is waiting for + # the file descriptor on 'address' + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(socket_name) + emsg = base64.b64encode(PASSFD_MSG) + eargs = base64.b64encode(address) + encoded = "%s|%s\n" % (emsg, eargs) + sock.send(encoded) + + diff --git a/test/resources/linux/ns3/cross_dce_linux_ccn.py b/test/resources/linux/ns3/cross_dce_linux_ccn.py new file mode 100755 index 00000000..c15ef4e5 --- /dev/null +++ b/test/resources/linux/ns3/cross_dce_linux_ccn.py @@ -0,0 +1,164 @@ +#!/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.resource import ResourceState, ResourceAction +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.set(node, "enableStack", True) + ec.register_connection(node, simu) + + 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) + + return dev + +def add_tap_device(ec, node, ip, prefix): + dev = ec.register_resource("linux::Tap") + ec.set(dev, "ip", ip) + ec.set(dev, "prefix", prefix) + ec.register_connection(node, dev) + + return dev + +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 + +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_cross_ccnpeek(self, host, user = None, identity = None): + ec = ExperimentController(exp_id = "test-linux-ns3-ccnpeek") + + node = ec.register_resource("linux::Node") + 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("linux::ns3::Simulation") + ec.set(simu, "simulatorImplementationType", "ns3::RealtimeSimulatorImpl") + ec.set(simu, "checksumEnabled", True) + ec.set(simu, "verbose", True) + #ec.set(simu, "buildMode", "debug") + #ec.set(simu, "nsLog", "FdNetDevice") + ec.register_connection(simu, node) + + nsnode = add_ns3_node(ec, simu) + + fddev = add_fd_device(ec, nsnode, "10.0.0.2", "30") + ec.enable_trace(fddev, "pcap") + ec.enable_trace(fddev, "promiscPcap") + ec.enable_trace(fddev, "ascii") + + tap = add_tap_device(ec, node, "10.0.0.1", "30") + + crosslink = ec.register_resource("linux::ns3::TunTapFdLink") + ec.register_connection(crosslink, tap) + ec.register_connection(crosslink, fddev) + + ### create DCE applications + ccnd1 = ec.register_resource("linux::ns3::dce::CCND") + ec.set(ccnd1, "stackSize", 1<<20) + ec.set(ccnd1, "StartTime", "1s") + ec.register_connection(ccnd1, nsnode) + + poke = ec.register_resource("linux::ns3::dce::CCNPoke") + ec.set(poke, "contentName", "ccnx:/chunk0") + ec.set(poke, "content", "DATA") + ec.set(poke, "stackSize", 1<<20) + ec.set(poke, "StartTime", "4s") + ec.register_connection(poke, nsnode) + + fib1 = ec.register_resource("linux::ns3::dce::FIBEntry") + ec.set(fib1, "protocol", "udp") + ec.set(fib1, "uri", "ccnx:/") + ec.set(fib1, "host", "10.0.0.1") + ec.set(fib1, "stackSize", 1<<20) + ec.set(fib1, "StartTime", "2s") + ec.register_connection(fib1, nsnode) + + ### create Linux applications + ccnd2 = ec.register_resource("linux::CCND") + ec.register_connection(ccnd2, node) + + peek = ec.register_resource("linux::CCNPeek") + ec.set(peek, "contentName", "ccnx:/chunk0") + ec.register_connection(peek, ccnd2) + + fib2 = ec.register_resource("linux::FIBEntry") + ec.set(fib2, "protocol", "udp") + ec.set(fib2, "uri", "ccnx:/") + ec.set(fib2, "host", "10.0.0.2") + ec.register_connection(fib2, ccnd2) + + ec.register_condition(peek, ResourceAction.START, poke, + ResourceState.STARTED) + + ec.deploy() + + ec.wait_finished([peek]) + + stdout = ec.trace(peek, "stdout") + expected = "DATA" + self.assertTrue(stdout.find(expected) > -1) + + ec.shutdown() + + def ztest_cross_ccnpeek_fedora(self): + self.t_cross_cnnpeek(self.fedora_host, self.fedora_user, self.fedora_identity) + + def test_cross_ccnpeek_local(self): + self.t_cross_ccnpeek("localhost") + + +if __name__ == '__main__': + unittest.main() + diff --git a/test/resources/linux/ns3/cross_ns3_linux_ping.py b/test/resources/linux/ns3/cross_ns3_linux_ping.py index 9c84930c..6c7a07d6 100755 --- a/test/resources/linux/ns3/cross_ns3_linux_ping.py +++ b/test/resources/linux/ns3/cross_ns3_linux_ping.py @@ -30,8 +30,10 @@ import unittest def add_ns3_node(ec, simu): node = ec.register_resource("ns3::Node") + ec.set(node, "enableStack", True) ec.register_connection(node, simu) + """ ipv4 = ec.register_resource("ns3::Ipv4L3Protocol") ec.register_connection(node, ipv4) @@ -43,6 +45,7 @@ def add_ns3_node(ec, simu): udp = ec.register_resource("ns3::UdpL4Protocol") ec.register_connection(node, udp) + """ return node @@ -79,6 +82,10 @@ class LinuxNS3FdNetDeviceTest(unittest.TestCase): 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_cross_ping(self, host, user = None, identity = None): ec = ExperimentController(exp_id = "test-linux-ns3-tap-fd") @@ -128,7 +135,7 @@ class LinuxNS3FdNetDeviceTest(unittest.TestCase): tap = add_tap_device(ec, node, "10.0.1.1", "30") - crosslink = ec.register_resource("linux::ns3::TapFdLink") + crosslink = ec.register_resource("linux::ns3::TunTapFdLink") ec.register_connection(crosslink, tap) ec.register_connection(crosslink, fddev) @@ -151,6 +158,7 @@ class LinuxNS3FdNetDeviceTest(unittest.TestCase): ec.wait_finished([app]) stdout = ec.trace(app, "stdout") + print stdout expected = "3 packets transmitted, 3 received, 0% packet loss" self.assertTrue(stdout.find(expected) > -1) @@ -164,6 +172,9 @@ class LinuxNS3FdNetDeviceTest(unittest.TestCase): def ztest_cross_ping_fedora(self): self.t_cross_ping(self.fedora_host, self.fedora_user, self.fedora_identity) + def ztest_cross_ping_ubuntu(self): + self.t_cross_ping(self.ubuntu_host, self.ubuntu_user, self.ubuntu_identity) + def test_cross_ping_local(self): self.t_cross_ping("localhost") diff --git a/test/resources/planetlab/ns3/cross_ns3_linux_ping.py b/test/resources/planetlab/ns3/cross_ns3_linux_ping.py new file mode 100755 index 00000000..4b44e5dc --- /dev/null +++ b/test/resources/planetlab/ns3/cross_ns3_linux_ping.py @@ -0,0 +1,159 @@ +#!/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.resource import ResourceState, ResourceAction +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.set(node, "enableStack", True) + ec.register_connection(node, simu) + + 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) + + return dev + +def add_tap_device(ec, node, ip, prefix): + dev = ec.register_resource("planetlab::Tap") + ec.set(dev, "ip", ip) + ec.set(dev, "prefix", prefix) + ec.register_connection(node, dev) + + return dev + +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 + +class LinuxNS3FdNetDeviceTest(unittest.TestCase): + def setUp(self): + #self.fedora_host = "nepi2.pl.sophia.inria.fr" + self.fedora_host = "planetlab2.sics.se" + # ple2.ipv6.lip6.fr + # inriarennes2.irisa.fr + # planetlab1.upc.es + # planetlab3.upc.es + self.fedora_user = "inria_nepi" + self.fedora_identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME']) + self.netblock = "192.168.3" + + @skipIfNotAlive + def t_cross_ping(self, host, user, identity): + ec = ExperimentController(exp_id = "test-pl-ns3-tap-fd") + + node = ec.register_resource("planetlab::Node") + 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("linux::ns3::Simulation") + ec.set(simu, "simulatorImplementationType", "ns3::RealtimeSimulatorImpl") + ec.set(simu, "checksumEnabled", True) + ec.set(simu, "verbose", True) + #ec.set(simu, "buildMode", "debug") + #ec.set(simu, "nsLog", "FdNetDevice") + ec.register_connection(simu, node) + + nsnode1 = add_ns3_node(ec, simu) + dev1 = add_point2point_device(ec, nsnode1, "%s.1" % self.netblock, "30") + + nsnode2 = add_ns3_node(ec, simu) + dev2 = add_point2point_device(ec, nsnode2, "%s.2" % self.netblock, "30") + + # Add routes on the NS3 side + r1 = ec.register_resource("ns3::Route") + ec.set(r1, "network", "%s.4" % self.netblock) + ec.set(r1, "prefix", "30") + ec.set(r1, "nexthop", "%s.1" % self.netblock) + ec.register_connection(r1, nsnode2) + + # Create channel + chan = ec.register_resource("ns3::PointToPointChannel") + ec.set(chan, "Delay", "0s") + ec.register_connection(chan, dev1) + ec.register_connection(chan, dev2) + + fddev = add_fd_device(ec, nsnode1, "%s.5" % self.netblock, "30") + ec.enable_trace(fddev, "pcap") + ec.enable_trace(fddev, "promiscPcap") + ec.enable_trace(fddev, "ascii") + + tap = add_tap_device(ec, node, "%s.6" % self.netblock, "30") + + crosslink = ec.register_resource("planetlab::ns3::TunTapFdLink") + ec.register_connection(crosslink, tap) + ec.register_connection(crosslink, fddev) + + r2 = ec.register_resource("planetlab::Vroute") + ec.set(r2, "action", "add") + ec.set(r2, "network", "%s.0/30" % self.netblock) + ec.register_connection(r2, tap) + + app = ec.register_resource("linux::Application") + ec.set(app, "command", "ping -c3 %s.1" % self.netblock) + ec.register_connection(app, node) + + ec.register_condition(app, ResourceAction.START, simu, + ResourceState.STARTED, time="5s") + + ec.deploy() + + ec.wait_finished([app]) + + stdout = ec.trace(app, "stdout") + expected = "3 packets transmitted, 3 received, 0% packet loss" + self.assertTrue(stdout.find(expected) > -1) + + ## Releasing to force ns3 to flush the traces + ec.release() + pcap = ec.trace(fddev, "pcap") + + self.assertTrue(len(pcap) > 4000) + ec.shutdown() + + def test_cross_ping_fedora(self): + self.t_cross_ping(self.fedora_host, self.fedora_user, self.fedora_identity) + + +if __name__ == '__main__': + unittest.main() + -- 2.43.0