Adding DCE PING example
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Wed, 16 Apr 2014 15:07:10 +0000 (17:07 +0200)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Wed, 16 Apr 2014 15:07:10 +0000 (17:07 +0200)
src/nepi/resources/linux/application.py
src/nepi/resources/linux/ns3/ns3dceapplication.py [new file with mode: 0644]
src/nepi/resources/linux/ns3/ns3simulation.py
test/resources/linux/ns3/ns3dceapplication.py [new file with mode: 0644]
test/resources/linux/ns3/ns3simulation.py

index 5adf1dc..4a90392 100644 (file)
@@ -275,7 +275,8 @@ class LinuxApplication(ResourceManager):
 
     def do_provision(self):
         # take a snapshot of the system if user is root
-        # to assure cleanProcess kill every nepi process
+        # to ensure that cleanProcess will not kill
+        # pre-existent processes
         if self.node.get("username") == 'root':
             import pickle
             procs = dict()
@@ -356,22 +357,23 @@ class LinuxApplication(ResourceManager):
                     env = env,
                     overwrite = overwrite)
 
-    def execute_deploy_command(self, command):
+    def execute_deploy_command(self, command, prefix="deploy"):
         if command:
             # Upload the command to a bash script and run it
             # in background ( but wait until the command has
             # finished to continue )
-            shfile = os.path.join(self.app_home, "deploy.sh")
+            shfile = os.path.join(self.app_home, "%s.sh" % prefix)
             self.node.run_and_wait(command, self.run_home,
                     shfile = shfile, 
                     overwrite = False,
-                    pidfile = "deploy_pidfile", 
-                    ecodefile = "deploy_exitcode", 
-                    stdout = "deploy_stdout", 
-                    stderr = "deploy_stderr")
-
-    def upload_sources(self, src_dir = None):
-        sources = self.get("sources")
+                    pidfile = "%s_pidfile" % prefix, 
+                    ecodefile = "%s_exitcode" % prefix, 
+                    stdout = "%s_stdout" % prefix, 
+                    stderr = "%s_stderr" % prefix)
+
+    def upload_sources(self, sources = None, src_dir = None):
+        if not sources:
+            sources = self.get("sources")
    
         command = ""
 
@@ -417,29 +419,33 @@ class LinuxApplication(ResourceManager):
 
         return command
 
-    def upload_files(self):
-        files = self.get("files")
+    def upload_files(self, files = None):
+        if not files:
+            files = self.get("files")
 
         if files:
             self.info("Uploading files %s " % files)
             self.node.upload(files, self.node.share_dir, overwrite = False)
 
-    def upload_libraries(self):
-        libs = self.get("libs")
+    def upload_libraries(self, libs = None):
+        if not libs:
+            libs = self.get("libs")
 
         if libs:
             self.info("Uploading libraries %s " % libaries)
             self.node.upload(libs, self.node.lib_dir, overwrite = False)
 
-    def upload_binaries(self):
-        bins = self.get("bins")
+    def upload_binaries(self, bins = None):
+        if not bins:
+            bins = self.get("bins")
 
         if bins:
             self.info("Uploading binaries %s " % binaries)
             self.node.upload(bins, self.node.bin_dir, overwrite = False)
 
-    def upload_code(self):
-        code = self.get("code")
+    def upload_code(self, code = None):
+        if not code:
+            code = self.get("code")
 
         if code:
             self.info("Uploading code")
@@ -447,8 +453,10 @@ class LinuxApplication(ResourceManager):
             dst = os.path.join(self.app_home, "code")
             self.node.upload(code, dst, overwrite = False, text = True)
 
-    def upload_stdin(self):
-        stdin = self.get("stdin")
+    def upload_stdin(self, stdin = None):
+        if not stdin:
+           stdin = self.get("stdin")
+
         if stdin:
             # create dir for sources
             self.info("Uploading stdin")
@@ -469,14 +477,17 @@ class LinuxApplication(ResourceManager):
 
             return command
 
-    def install_dependencies(self):
-        depends = self.get("depends")
+    def install_dependencies(self, depends = None):
+        if not depends:
+            depends = self.get("depends")
+
         if depends:
             self.info("Installing dependencies %s" % depends)
             return self.node.install_packages_command(depends)
 
-    def build(self):
-        build = self.get("build")
+    def build(self, build = None):
+        if not build:
+            build = self.get("build")
 
         if build:
             self.info("Building sources ")
@@ -484,8 +495,9 @@ class LinuxApplication(ResourceManager):
             # replace application specific paths in the command
             return self.replace_paths(build)
 
-    def install(self):
-        install = self.get("install")
+    def install(self, install = None):
+        if not install:
+            install = self.get("install")
 
         if install:
             self.info("Installing sources ")
diff --git a/src/nepi/resources/linux/ns3/ns3dceapplication.py b/src/nepi/resources/linux/ns3/ns3dceapplication.py
new file mode 100644 (file)
index 0000000..bf1a6e3
--- /dev/null
@@ -0,0 +1,80 @@
+#
+#    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, ResourceState, reschedule_delay
+from nepi.resources.ns3.ns3dceapplication import NS3BaseDceApplication
+
+@clsinit_copy
+class NS3LinuxDceApplication(NS3BaseDceApplication):
+    _rtype = "ns3::LinuxDceApplication"
+
+    @classmethod
+    def _register_attributes(cls):
+        sources = Attribute("sources",
+                "Path to tar.gz file with sources for the application execute in DCE. "
+                "Sources will be uploaded to ${SRC} and it is the responsibility of "
+                "the build instructions (in the build attribute) to copy the compiled "
+                "binaries to the ${BIN_DCE} directory",
+                flags = Flags.Design)
+
+        build = Attribute("build",
+                "Instructions to compile sources DCE-compatible way. "
+                "Note that sources will be uploaded to ${SRC} and the "
+                "build instructions are responsible for copying the "
+                "binaries to the ${BIN_DCE} directory. ",
+                flags = Flags.Design)
+
+        starttime = Attribute("StartTime",
+            "Time at which the application will start",
+            default = "+0.0ns",  
+            flags = Flags.Reserved | Flags.Construct)
+
+
+        stoptime = Attribute("StopTime",
+            "Time at which the application will stop",
+            default = "+0.0ns",  
+            flags = Flags.Reserved | Flags.Construct)
+
+        cls._register_attribute(sources)
+        cls._register_attribute(build)
+        cls._register_attribute(stoptime)
+        cls._register_attribute(starttime)
+
+    def _instantiate_object(self):
+        command = []
+        
+        sources = self.get("sources")
+        if sources:
+            self.info("Uploading sources %s " % sources)
+            scmd = self.simulation.upload_extra_sources(sources = sources)
+            if scmd:
+                command.append(scmd)
+                
+        build = self.get("build")
+        if build:
+            bcmd = self.simulation.build(build = build)
+            if bcmd:
+                command.append(bcmd)
+
+        if command:
+            deploy_command = ";".join(command)
+            prefix = "%d_deploy" % self.guid 
+            self.simulation.execute_deploy_command(deploy_command, prefix=prefix)
+
index 25b9535..9e1ee51 100644 (file)
@@ -187,6 +187,11 @@ class LinuxNS3Simulation(LinuxApplication, NS3Simulation):
         src_dir = os.path.join(self.node.src_dir, "ns-3")
 
         super(LinuxNS3Simulation, self).upload_sources(src_dir = src_dir)
+    
+    def upload_extra_sources(self, sources = None, src_dir = None):
+        return super(LinuxNS3Simulation, self).upload_sources(
+                sources = sources, 
+                src_dir = src_dir)
 
     def upload_start_command(self):
         command = self.get("command")
@@ -545,14 +550,38 @@ class LinuxNS3Simulation(LinuxApplication, NS3Simulation):
         env.append("PYTHONPATH=$PYTHONPATH:${NS3BINDINGS:=%(ns3_build_home)s/lib/python/site-packages}" % { 
                     'ns3_build_home': self.ns3_build_home
                  })
-        env.append("LD_LIBRARY_PATH=${NS3LIBRARIES:=%(ns3_build_home)s/lib/}" % { 
+        # If NS3LIBRARIES is defined and not empty, assign its value, 
+        # if not assign ns3_build_home/lib/ to NS3LIBRARIES and LD_LIBARY_PATH
+        env.append("LD_LIBRARY_PATH=${NS3LIBRARIES:=%(ns3_build_home)s/lib}" % { 
                     'ns3_build_home': self.ns3_build_home
                  })
-        env.append("DCE_PATH=$PATH:$NS3LIBRARIES/../bin_dce")
-        env.append("DCE_ROOT=$PATH:$NS3LIBRARIES/..")
+        env.append("DCE_PATH=$NS3LIBRARIES/../bin_dce")
+        env.append("DCE_ROOT=$NS3LIBRARIES/..")
 
         return " ".join(env) 
 
+    def replace_paths(self, command):
+        """
+        Replace all special path tags with shell-escaped actual paths.
+        """
+        return ( command
+            .replace("${USR}", self.node.usr_dir)
+            .replace("${LIB}", self.node.lib_dir)
+            .replace("${BIN}", self.node.bin_dir)
+            .replace("${SRC}", self.node.src_dir)
+            .replace("${SHARE}", self.node.share_dir)
+            .replace("${EXP}", self.node.exp_dir)
+            .replace("${EXP_HOME}", self.node.exp_home)
+            .replace("${APP_HOME}", self.app_home)
+            .replace("${RUN_HOME}", self.run_home)
+            .replace("${NODE_HOME}", self.node.node_home)
+            .replace("${HOME}", self.node.home_dir)
+            # If NS3LIBRARIES is defined and not empty, use that value, 
+            # if not use ns3_build_home/lib/
+            .replace("${BIN_DCE}", "${NS3LIBRARIES-%s/lib/}../bin_dce" % \
+                    self.ns3_build_home)
+            )
+
     def valid_connection(self, guid):
         # TODO: Validate!
         return True
diff --git a/test/resources/linux/ns3/ns3dceapplication.py b/test/resources/linux/ns3/ns3dceapplication.py
new file mode 100644 (file)
index 0000000..21b046e
--- /dev/null
@@ -0,0 +1,218 @@
+#!/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
+
+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_point2point_device(ec, node, address = None,  prefix = None):
+    dev = ec.register_resource("ns3::PointToPointNetDevice")
+    if address:
+       ec.set(dev, "ip", address)
+    if prefix:
+       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, address = None, prefix = None):
+    dev = ec.register_resource("ns3::CsmaNetDevice")
+    if address:
+        ec.set(dev, "ip", address)
+    if prefix:
+        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, address = None, prefix = None, 
+        access_point = False):
+    dev = ec.register_resource("ns3::WifiNetDevice")
+    if address:
+        ec.set(dev, "ip", address)
+    if prefix:
+        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 LinuxNS3DceApplicationTest(unittest.TestCase):
+    def setUp(self):
+        #elf.fedora_host = "nepi2.pl.sophia.inria.fr"
+        #self.fedora_host = "planetlabpc1.upf.edu"
+        self.fedora_host = "peeramide.irisa.fr"
+        self.fedora_user = "inria_nepi"
+        #self.fedora_user = "inria_alina"
+        self.fedora_identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'])
+
+    def test_dce_application(self):
+        ec = ExperimentController(exp_id = "test-linux-ns3-dce")
+        
+        node = ec.register_resource("LinuxNode")
+        ec.set(node, "hostname", self.fedora_host)
+        ec.set(node, "username", self.fedora_user)
+        ec.set(node, "identity", self.fedora_identity)
+        ec.set(node, "cleanProcesses", True)
+        #ec.set(node, "cleanHome", True)
+
+        simu = ec.register_resource("LinuxNS3Simulation")
+        ec.set(simu, "verbose", True)
+        ec.set(simu, "enableDCE", True)
+        ec.set(simu, "buildMode", "debug")
+        ec.set(simu, "nsLog", "DceApplication")
+        ec.register_connection(simu, node)
+
+        nsnode1 = add_ns3_node(ec, simu)
+        ec.set(nsnode1, "enableDCE", True)
+        p2p1 = add_point2point_device(ec, nsnode1, "10.0.0.1", "30")
+        ec.set(p2p1, "DataRate", "5Mbps")
+
+        nsnode2 = add_ns3_node(ec, simu)
+        ec.set(nsnode2, "enableDCE", True)
+        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
+        ping = ec.register_resource("ns3::LinuxDceApplication")
+        """
+        ec.set (ping, "sources", "http://www.skbuff.net/iputils/iputils-s20101006.tar.bz2")
+        ec.set (ping, "build", "tar xvjf ${SRC}/iputils-s20101006.tar.bz2 && "
+                "cd iputils-s20101006/ && "
+                "sed -i 's/CFLAGS=/CFLAGS+=/g' Makefile && "
+                "make CFLAGS=-fPIC LDFLAGS=-pie ping && "
+                "cp ping ${BIN_DCE} ")
+        """
+        ec.set (ping, "binary", "ping")
+        ec.set (ping, "stackSize", 1<<20)
+        ec.set (ping, "arguments", "-c 10;-s 1000;10.0.0.2")
+        ec.set (ping, "StartTime", "1s")
+        ec.set (ping, "StopTime", "20s")
+        ec.register_connection(ping, nsnode1)
+
+        ec.deploy()
+
+        ec.wait_finished([ping])
+
+        expected = "ping -c 10 -s 1000 10.0.0.2"
+        cmdline = ec.trace(ping, "cmdline")
+        self.assertTrue(cmdline.find(expected) > -1, cmdline)
+        
+        expected = "Start Time: NS3 Time:          1s ("
+        status = ec.trace(ping, "status")
+        self.assertTrue(status.find(expected) > -1, status)
+
+        expected = "10 packets transmitted, 10 received, 0% packet loss, time 9002ms"
+        stdout = ec.trace(ping, "stdout")
+        self.assertTrue(stdout.find(expected) > -1, stdout)
+
+        stderr = ec.trace(simu, "stderr")
+        expected = "DceApplication:StartApplication"
+        self.assertTrue(stderr.find(expected) > -1, stderr)
+
+        ec.shutdown()
+
+
+if __name__ == '__main__':
+    unittest.main()
index 78c73f8..cf4a7d9 100644 (file)
 #
 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
 
-
-#
-# Network topology
-#
-#       n0    n1   n2   n3
-#       |     |    |    |
-#       -----------------
-#
-#  node n0 sends IGMP traffic to node n3
-
-
 from nepi.execution.ec import ExperimentController 
 from nepi.execution.trace import TraceAttr
 
@@ -141,7 +130,7 @@ def add_wifi_channel(ec):
 
     return channel
 
-class LinuxNS3ClientTest(unittest.TestCase):
+class LinuxNS3SimulationTest(unittest.TestCase):
     def setUp(self):
         #elf.fedora_host = "nepi2.pl.sophia.inria.fr"
         #self.fedora_host = "planetlabpc1.upf.edu"