Added LinuxTcpdump
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Fri, 12 Jul 2013 01:44:39 +0000 (18:44 -0700)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Fri, 12 Jul 2013 01:44:39 +0000 (18:44 -0700)
src/nepi/execution/ec.py
src/nepi/resources/linux/ccn/fibentry.py
src/nepi/resources/linux/mtr.py
src/nepi/resources/linux/ping.py
src/nepi/resources/linux/tcpdump.py [new file with mode: 0644]
test/resources/linux/ccn/fibentry.py [new file with mode: 0644]
test/resources/linux/tcpdump.py [new file with mode: 0755]

index abcb88d..645d667 100644 (file)
@@ -537,7 +537,7 @@ class ExperimentController(object):
 
         """
         rm = self.get_resource(guid)
-        return rm.start_with_condition()
+        return rm.start_with_conditions()
 
     def deploy(self, group = None, wait_all_ready = True):
         """ Deploy all resource manager in group
index 83fbe9c..ace4f52 100644 (file)
@@ -67,12 +67,16 @@ class LinuxFIBEntry(LinuxApplication):
     @classmethod
     def _register_traces(cls):
         ping = Trace("ping", "Continuous ping to the peer end")
+        mtr = Trace("mtr", "Continuous mtr to the peer end")
 
         cls._register_trace(ping)
+        cls._register_trace(mtr)
 
     def __init__(self, ec, guid):
         super(LinuxFIBEntry, self).__init__(ec, guid)
         self._home = "fib-%s" % self.guid
+        self._ping = None
+        self._mtr = None
 
     @property
     def ccnd(self):
@@ -85,6 +89,14 @@ class LinuxFIBEntry(LinuxApplication):
         if self.ccnd: return self.ccnd.node
         return None
 
+    def trace(self, name, attr = TraceAttr.ALL, block = 512, offset = 0):
+        if name == "ping":
+            return self.ec.trace(self._ping, "stdout", attr, block, offset)
+        if name == "mtr":
+            return self.ec.trace(self._mtr, "stdout", attr, block, offset)
+
+        return super(LinuxFIBEntry, self).trace(name, attr, block, offset)
+        
     def deploy(self):
         # Wait until associated ccnd is provisioned
         if not self.ccnd or self.ccnd.state < ResourceState.READY:
@@ -135,41 +147,36 @@ class LinuxFIBEntry(LinuxApplication):
                 raise RuntimeError, msg
 
     def configure(self):
-        if not self.trace_enabled("ping"):
-            return
-
-        ping_script = """echo "Staring PING %(host)s at date `date +'%Y%m%d%H%M%S'`"; ping %(host)s""" % ({
-            "host": self.get("host")}) 
-        ping_file = os.path.join(self.run_home, "ping.sh")
-        self.node.upload(ping_script,
-                ping_file,
-                text = True, 
-                overwrite = False)
-
-        command = """bash %s""" % ping_file
-        (out, err), proc = self.node.run(command, self.run_home, 
-            stdout = "ping",
-            stderr = "ping_stderr",
-            pidfile = "ping_pidfile")
-
-        # Wait for pid file to be generated
-        pid, ppid = self.node.wait_pid(self.run_home, "ping_pidfile")
-
-        # If the process is not running, check for error information
-        # on the remote machine
-        if not pid or not ppid:
-            (out, err), proc = self.node.check_errors(self.run_home,
-                    stderr = "ping_pidfile") 
-
-            # Out is what was written in the stderr file
-            if err:
-                self.fail()
-                msg = " Failed to deploy ping trace command '%s' " % command
-                self.error(msg, out, err)
-                raise RuntimeError, msg
-
-            #while true; do echo `date +'%Y%m%d%H%M%S'`; mtr --no-dns --report -c 1 roseval.pl.sophia.inria.fr;sleep 2;done
+        if self.trace_enabled("ping"):
+            self.info("Configuring PING trace")
+            self._ping = self.ec.register_resource("LinuxPing")
+            self.ec.set(self._ping, "printTimestamp", True)
+            self.ec.set(self._ping, "target", self.get("host"))
+            self.ec.register_connection(self._ping, self.node.guid)
+            # force waiting until ping is READY before we starting the FIB
+            self.ec.register_condition(self.guid, ResourceAction.START, 
+                    self._ping, ResourceState.READY)
+            # schedule ping deploy and start
+            self.ec.deploy(group=[self._ping])
+            self.ec.start_with_conditions(self._ping)
+
+        if self.trace_enabled("mtr"):
+            self.info("Configuring TRACE trace")
+            self._mtr = self.ec.register_resource("LinuxMtr")
+            self.ec.set(self._mtr, "noDns", True)
+            self.ec.set(self._mtr, "printTimestamp", True)
+            self.ec.set(self._mtr, "continuous", True)
+            self.ec.set(self._mtr, "target", self.get("host"))
+            self.ec.register_connection(self._mtr, self.node.guid)
+            self.ec.deploy(group=[self._mtr])
+            # force waiting until mtr is READY before we starting the FIB
+            self.ec.register_condition(self.guid, ResourceAction.START, 
+                    self._mtr, ResourceState.READY)
+            # schedule mtr deploy and start
+            self.ec.deploy(group=[self._mtr])
+            self.ec.start_with_conditions(self._mtr)
+
+    
     def start(self):
         if self._state in [ResourceState.READY, ResourceState.STARTED]:
             command = self.get("command")
@@ -196,11 +203,6 @@ class LinuxFIBEntry(LinuxApplication):
             if proc.poll():
                 pass
 
-            # now stop the ping trace
-            if self.trace_enabled("ping"):
-               pid, ppid = self.node.wait_pid(self.run_home, "ping_pidfile")
-               (out, err), proc = self.node.kill(pid, ppid)
-
             self._stop_time = tnow()
             self._state = ResourceState.STOPPED
 
index 0bf5fae..9de7ca1 100644 (file)
@@ -54,6 +54,18 @@ class LinuxMtr(LinuxApplication):
             "between ICMP ECHO requests. Default value is one second ",
             flags = Flags.ExecReadOnly)
 
+        countinuous = Attribute("continuous",
+            "Run mtr in a while loop",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        print_timestamp = Attribute("printTimestamp",
+            "Print timestamp before running mtr",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
         target = Attribute("target",
             "mtr target host (host that will be pinged)",
             flags = Flags.ExecReadOnly)
@@ -62,11 +74,14 @@ class LinuxMtr(LinuxApplication):
         cls._register_attribute(no_dns)
         cls._register_attribute(address)
         cls._register_attribute(interval)
+        cls._register_attribute(countinuous)
+        cls._register_attribute(print_timestamp)
         cls._register_attribute(target)
 
     def __init__(self, ec, guid):
         super(LinuxMtr, self).__init__(ec, guid)
         self._home = "mtr-%s" % self.guid
+        self._sudo_kill = True
 
     def deploy(self):
         if not self.get("command"):
@@ -80,6 +95,11 @@ class LinuxMtr(LinuxApplication):
     @property
     def _start_command(self):
         args = []
+        if self.get("continuous") == True:
+            args.append("while true; do ")
+        if self.get("printTimestamp") == True:
+            args.append("""echo "`date +'%Y%m%d%H%M%S'`";""")
+        args.append("sudo -S mtr --report")
         if self.get("reportCycles"):
             args.append("-c %s" % self.get("reportCycles"))
         if self.get("noDns") == True:
@@ -87,10 +107,10 @@ class LinuxMtr(LinuxApplication):
         if self.get("address"):
             args.append("-a %s" % self.get("address"))
         args.append(self.get("target"))
+        if self.get("continuous") == True:
+            args.append("; done ")
 
-        command = """echo "Starting mtr `date +'%Y%m%d%H%M%S'`"; """
-        command += " sudo -S mtr --report "
-        command += " ".join(args)
+        command = " ".join(args)
 
         return command
 
index 0a5796a..a80397b 100644 (file)
@@ -56,7 +56,7 @@ class LinuxPing(LinuxApplication):
 
         numeric = Attribute("numeric",
             "Sets ping -n option. Disables resolution of host addresses into "
-            "symbolic names ",
+            "symbolic names. ",
             type = Types.Bool,
             default = False,
             flags = Flags.ExecReadOnly)
@@ -193,6 +193,11 @@ class LinuxPing(LinuxApplication):
     def _start_command(self):
         args = []
 
+        if self.get("printTimestamp") == True:
+            args.append("""echo "`date +'%Y%m%d%H%M%S'`";""")
+
+        args.append("ping ")
+        
         if self.get("count"):
             args.append("-c %s" % self.get("count"))
         if self.get("mark"):
@@ -207,8 +212,6 @@ class LinuxPing(LinuxApplication):
             args.append("-n")
         if self.get("pattern"):
             args.append("-p %s" % self.get("pattern"))
-        if self.get("printTimestamp") == True:
-            args.append("-D")
         if self.get("tos"):
             args.append("-Q %s" % self.get("tos"))
         if self.get("quiet"):
@@ -239,8 +242,7 @@ class LinuxPing(LinuxApplication):
             args.append("-W %s" % self.get("timeout"))
         args.append(self.get("target"))
 
-        command = " sudo -S ping "
-        command += " ".join(args)
+        command = " ".join(args)
 
         return command
 
diff --git a/src/nepi/resources/linux/tcpdump.py b/src/nepi/resources/linux/tcpdump.py
new file mode 100644 (file)
index 0000000..06357de
--- /dev/null
@@ -0,0 +1,406 @@
+#
+#    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.attribute import Attribute, Flags, Types
+from nepi.execution.resource import clsinit_copy 
+from nepi.resources.linux.application import LinuxApplication
+from nepi.util.timefuncs import tnow
+
+import os
+
+@clsinit_copy
+class LinuxTcpdump(LinuxApplication):
+    _rtype = "LinuxTcpdump"
+
+    @classmethod
+    def _register_attributes(cls):
+        A = Attribute("A",
+            "Sets tcpdump -A option. "
+            "Prints each packet (minus its link level header) in ASCII.",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        b = Attribute("b",
+            "Sets tcpdump -b option. "
+            "Prints the AS number in BGP packets in ASDOT notation. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        B = Attribute("B",
+            "Sets tcpdump -B option. "
+            "Sets the operaing system capture buffer size in untils of "
+            "KiB (1024 bytes).",
+            flags = Flags.ExecReadOnly)
+
+        c = Attribute("c",
+            "Sets tcpdump -c option. "
+            "Exists after receiving count packets.",
+            flags = Flags.ExecReadOnly)
+
+        C = Attribute("C",
+            "Sets tcpdump -C option. "
+            "Before writing a raw packet to a savefile, check whether the "
+            "file is currently larger than file_size and, if so, close the "
+            "current  savefile  and  open a new one. "
+            "Savefiles after the first savefile will have the name specified "
+            "with the -w with a number after it, starting at 1 and continuing "
+            "upward. ",
+            flags = Flags.ExecReadOnly)
+
+        d = Attribute("d",
+            "Sets tcpdump -d option. "
+            "Dump the compiled packet-matching code in a human readable form "
+            "to standard output and stop.",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        dd = Attribute("dd",
+            "Sets tcpdump -dd option. "
+            "Dump packet-matching code as a C program fragment. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        ddd = Attribute("ddd",
+            "Sets tcpdump -ddd option. "
+            "Dump packet-matching code as decimal numbers "
+            "(preceded with a count).",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        D = Attribute("D",
+            "Sets tcpdump -D option. "
+            "Print the list of the network interfaces available on the system "
+            "and on which tcpdump can capture packets. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        e = Attribute("e",
+            "Sets tcpdump -e option. "
+            "Print the link-level header on each dump line.",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        F =  Attribute("F",
+            "Sets tcpdump -F option. "
+            "Use file as input for the filter expression.",
+            flags = Flags.ExecReadOnly)
+
+        G =  Attribute("G",
+            "Sets tcpdump -G option. "
+            "If specified, rotates the dump file specified with the -w "
+            "option every rotate_seconds seconds. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        i =  Attribute("i",
+            "Sets tcpdump -i option. "
+            "Listen on interface.  If unspecified, tcpdump searches the "
+            "system interface list for the lowest  numbered, configured "
+            "up interface (excluding loopback). ",
+            flags = Flags.ExecReadOnly)
+
+        I =  Attribute("I",
+            "Sets tcpdump -I option. "
+            "Put the interface in 'monitor mode'; this is supported only "
+            "on IEEE 802.11 Wi-Fi interfaces, and supported only on some "
+            "operating systems. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        j = Attribute("j",
+            "Sets tcpdump -j option. "
+            "Sets the time stamp type for the capture to tstamp_type. "
+            "The names to use for the time stamp types are given in "
+            "pcap-tstamp-type(7); not all the types listed there will "
+            "necessarily be valid for any given interface.",
+            flags = Flags.ExecReadOnly)
+
+        K = Attribute("K",
+            "Sets tcpdump -K option. "
+            "Don't attempt to verify IP, TCP, or UDP checksums. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        l = Attribute("l",
+            "Sets tcpdump -l option. "
+            "Make stdout line buffered. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        U = Attribute("U",
+            "Sets tcpdump -U option. "
+            "Similar to -l in its behavior, but it will cause output to be "
+            "``packet-buffered'', so that the output is written to stdout "
+            "at the end of each packet. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        n = Attribute("n",
+            "Sets tcpdump -n option. "
+            "Don't convert addresses (i.e., host addresses, port numbers, "
+            "etc.) to names.",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        N = Attribute("N",
+            "Sets tcpdump -N option. "
+            "Don't  print domain name qualification of host names. "
+            "E.g., if you give this flag then tcpdump will print ``nic'' " 
+            "instead of ``nic.ddn.mil''.",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        S = Attribute("S",
+            "Sets tcpdump -S option. "
+            "Print absolute, rather than relative, TCP sequence numbers.",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        s = Attribute("s",
+            "Sets tcpdump -s option. "
+            "Snarf  snaplen bytes of data from each packet rather than "
+            "the default of 65535 bytes. ",
+            flags = Flags.ExecReadOnly)
+
+        T = Attribute("T",
+            "Sets tcpdump -T option. "
+             "Force packets selected by 'expression' to be interpreted the "
+             "specified type.  Currently known types are aodv  (Ad-hoc "
+             "On-demand  Distance Vector protocol), cnfp (Cisco NetFlow "
+             "protocol), rpc (Remote Procedure Call), rtp (Real-Time "
+             "Applications protocol), rtcp (Real-Time Applications control "
+             "protocol), snmp (Simple Network Management Protocol), tftp "
+             "(Trivial  File Transfer Protocol), vat (Visual Audio Tool), "
+             "and wb (distributed White Board).",
+            flags = Flags.ExecReadOnly)
+
+        t = Attribute("t",
+            "Sets tcpdump -t option. "
+            "Don't print a timestamp on each dump line.",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        tt = Attribute("tt",
+            "Sets tcpdump -tt option. "
+            "Print an unformatted timestamp on each dump line. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        ttt = Attribute("ttt",
+            "Sets tcpdump -ttt option. "
+            "Print a delta (micro-second resolution) between current "
+            "and previous line on each dump line.",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        tttt = Attribute("tttt",
+            "Sets tcpdump -tttt option. "
+            "Print a timestamp in default format proceeded by date on "
+            "each dump line. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        ttttt = Attribute("ttttt",
+            "Sets tcpdump -ttttt option. "
+            "Print a delta (micro-second resolution) between current and "
+            "first line on each dump line.",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        v = Attribute("v",
+            "Sets tcpdump -v option. "
+            "When  parsing  and printing, produce (slightly more) "
+            "verbose output. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        vv = Attribute("vv",
+            "Sets tcpdump -vv option. "
+            "Even  more  verbose  output. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        vvv = Attribute("vvv",
+            "Sets tcpdump -vv option. "
+            "Even  more  verbose  output. ",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        w = Attribute("w",
+            "Sets tcpdump -w option. "
+            "Write  the  raw  packets to file rather than parsing "
+            "and printing them out.",
+            type = Types.Bool,
+            default = False,
+            flags = Flags.ExecReadOnly)
+
+        expression = Attribute("expression",
+            "selects  which packets will be dumped.",
+            flags = Flags.ExecReadOnly)
+
+        cls._register_attribute(A)
+        cls._register_attribute(b)
+        cls._register_attribute(B)
+        cls._register_attribute(c)
+        cls._register_attribute(C)
+        cls._register_attribute(d)
+        cls._register_attribute(dd)
+        cls._register_attribute(ddd)
+        cls._register_attribute(D)
+        cls._register_attribute(e)
+        cls._register_attribute(F)
+        cls._register_attribute(G)
+        cls._register_attribute(i)
+        cls._register_attribute(I)
+        cls._register_attribute(j)
+        cls._register_attribute(K)
+        cls._register_attribute(l)
+        cls._register_attribute(U)
+        cls._register_attribute(n)
+        cls._register_attribute(N)
+        cls._register_attribute(S)
+        cls._register_attribute(s)
+        cls._register_attribute(T)
+        cls._register_attribute(t)
+        cls._register_attribute(tt)
+        cls._register_attribute(ttt)
+        cls._register_attribute(tttt)
+        cls._register_attribute(ttttt)
+        cls._register_attribute(v)
+        cls._register_attribute(vv)
+        cls._register_attribute(vvv)
+        cls._register_attribute(w)
+        cls._register_attribute(expression)
+
+    def __init__(self, ec, guid):
+        super(LinuxTcpdump, self).__init__(ec, guid)
+        self._home = "tcpdump-%s" % self.guid
+        self._sudo_kill = True
+
+    def deploy(self):
+        if not self.get("command"):
+            self.set("command", self._start_command)
+
+        if not self.get("depends"):
+            self.set("depends", "tcpdump")
+
+        super(LinuxTcpdump, self).deploy()
+
+    @property
+    def _start_command(self):
+        args = []
+        args.append("sudo -S tcpdump")
+        if self.get("A") == True:
+            args.append("-A")
+        if self.get("b") == True:
+            args.append("-b")
+        if self.get("B"):
+            args.append("-B %s" % self.get("B"))
+        if self.get("c"):
+            args.append("-c %s" % self.get("c"))
+        if self.get("C"):
+            args.append("-C %s" % self.get("C"))
+        if self.get("d") == True:
+            args.append("-d")
+        if self.get("dd") == True:
+            args.append("-dd")
+        if self.get("ddd") == True:
+            args.append("-ddd")
+        if self.get("D") == True:
+            args.append("-D")
+        if self.get("e") == True:
+            args.append("-e")
+        if self.get("F"):
+            args.append("-F %s" % self.get("F"))
+        if self.get("G") == True:
+            args.append("-G")
+        if self.get("i"):
+            args.append("-i %s" % self.get("i"))
+        if self.get("I") == True:
+            args.append("-I")
+        if self.get("j"):
+            args.append("-j %s" % self.get("j"))
+        if self.get("K") == True:
+            args.append("-K")
+        if self.get("l") == True:
+            args.append("-l")
+        if self.get("U") == True:
+            args.append("-U")
+        if self.get("n") == True:
+            args.append("-n")
+        if self.get("N") == True:
+            args.append("-N")
+        if self.get("S") == True:
+            args.append("-S")
+        if self.get("s"):
+            args.append("-s %s" % self.get("s"))
+        if self.get("T"):
+            args.append("-T %s" % self.get("T"))
+        if self.get("t") == True:
+            args.append("-t")
+        if self.get("tt") == True:
+            args.append("-tt")
+        if self.get("ttt") == True:
+            args.append("-ttt")
+        if self.get("tttt") == True:
+            args.append("-tttt")
+        if self.get("ttttt") == True:
+            args.append("-ttttt")
+        if self.get("v") == True:
+            args.append("-v")
+        if self.get("vv") == True:
+            args.append("-vv")
+        if self.get("vvv") == True:
+            args.append("-vvv")
+        if self.get("w"):
+            args.append("-w %s" % self.get("w"))
+        if self.get("expression"):
+            args.append(self.get("expression"))
+
+        command = " ".join(args)
+
+        return command
+
+    def valid_connection(self, guid):
+        # TODO: Validate!
+        return True
+
diff --git a/test/resources/linux/ccn/fibentry.py b/test/resources/linux/ccn/fibentry.py
new file mode 100644 (file)
index 0000000..cf2f8bb
--- /dev/null
@@ -0,0 +1,94 @@
+#!/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 test_utils import skipIfNotAlive, skipInteractive
+
+import os
+import time
+import tempfile
+import unittest
+
+class LinuxFIBEntryTestCase(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 = "alina"
+        
+        self.target = "nepi5.pl.sophia.inria.fr"
+
+    @skipIfNotAlive
+    def t_traces(self, host, user):
+
+        ec = ExperimentController(exp_id = "test-fib-traces")
+        
+        node1 = ec.register_resource("LinuxNode")
+        ec.set(node1, "hostname", host)
+        ec.set(node1, "username", user)
+        #ec.set(node1, "cleanHome", True)
+        ec.set(node1, "cleanProcesses", True)
+
+        ccnd1 = ec.register_resource("LinuxCCND")
+        ec.register_connection(ccnd1, node1)
+
+        entry1 = ec.register_resource("LinuxFIBEntry")
+        ec.set(entry1, "host", self.target)
+        ec.enable_trace(entry1, "ping")
+        ec.enable_trace(entry1, "mtr")
+        ec.register_connection(entry1, ccnd1)
+        node2 = ec.register_resource("LinuxNode")
+        ec.set(node2, "hostname", self.target)
+        ec.set(node2, "username", self.fedora_user)
+        ec.set(node2, "cleanHome", True)
+        ec.set(node2, "cleanProcesses", True)
+
+        ccnd2 = ec.register_resource("LinuxCCND")
+        ec.register_connection(ccnd2, node2)
+
+        entry2 = ec.register_resource("LinuxFIBEntry")
+        ec.set(entry2, "host", host)
+        ec.register_connection(entry2, ccnd2)
+
+        ec.deploy()
+
+        ec.wait_started([ccnd1, ccnd2])
+
+        stdout = ec.trace(entry1, "ping")
+        expected = "icmp_seq=1" 
+        self.assertTrue(stdout.find(expected) > -1)
+
+        stdout = ec.trace(entry1, "mtr")
+        expected = "1." 
+        self.assertTrue(stdout.find(expected) > -1)
+
+        ec.shutdown()
+
+    def test_traces_fedora(self):
+        self.t_traces(self.fedora_host, self.fedora_user)
+
+    def test_traces_ubuntu(self):
+        self.t_traces(self.ubuntu_host, self.ubuntu_user)
+
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/test/resources/linux/tcpdump.py b/test/resources/linux/tcpdump.py
new file mode 100755 (executable)
index 0000000..6ba7375
--- /dev/null
@@ -0,0 +1,76 @@
+#!/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 test_utils import skipIfNotAlive, skipInteractive
+
+import os
+import time
+import tempfile
+import unittest
+
+class LinuxTcpdumpTestCase(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 = "alina"
+        
+        self.target = "nepi5.pl.sophia.inria.fr"
+
+    @skipIfNotAlive
+    def t_tofile(self, host, user):
+        ec = ExperimentController(exp_id = "test-to-file")
+        
+        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)
+
+        pcap = ec.register_resource("LinuxTcpdump")
+        ec.set(pcap, "i", "eth0")
+        ec.set(pcap, "w", "custom_output")
+        ec.register_connection(pcap, node)
+
+        app = ec.register_resource("LinuxPing")
+        ec.set(app, "count", "20")
+        ec.set(app, "target", self.target)
+        ec.register_connection(app, node)
+
+        ec.deploy()
+
+        ec.wait_finished(app)
+
+        output = ec.trace(pcap, "custom_output")
+        self.assertTrue(len(output) > 0)
+
+        ec.shutdown()
+
+    def test_tofile_fedora(self):
+        self.t_tofile(self.fedora_host, self.fedora_user)
+
+    def test_tofile_ubuntu(self):
+        self.t_tofile(self.ubuntu_host, self.ubuntu_user)
+
+if __name__ == '__main__':
+    unittest.main()
+