COVERAGE = $(or $(shell which coverage), $(shell which python-coverage), \
coverage)
-all:
+all: clean
PYTHONPATH="$(PYTHONPATH):$(SRCDIR)" ./setup.py build
install: all
rm -f .coverage
clean:
- ./setup.py clean
rm -f `find -name \*.pyc` .coverage *.pcap
+ rm -rf "$(BUILDDIR)"
distclean: clean
rm -rf "$(DISTDIR)"
package_dir = {"": "src"},
package_data = {
"nepi.resources.planetlab" : [ "scripts/*.py" ],
- "nepi.resources.all" : [ "scripts/*.py" ]
+ "nepi.resources.linux" : [ "scripts/*.py" ]
}
)
import logging
err = traceback.format_exc()
logger = logging.getLogger("Resource.find_types()")
- logger.error("Error while lading Resource Managers %s" % err)
+ logger.error("Error while loading Resource Managers %s" % err)
return types
# Only try to kill the process if the pid and ppid
# were retrieved
if self.pid and self.ppid:
- (out, err), proc = self.node.kill(self.pid, self.ppid, sudo =
- self._sudo_kill)
+ (out, err), proc = self.node.kill(self.pid, self.ppid,
+ sudo = self._sudo_kill)
- if out or err:
+ if proc.poll() or err:
# check if execution errors occurred
msg = " Failed to STOP command '%s' " % self.get("command")
self.error(msg, out, err)
else:
# We need to query the status of the command we launched in
- # background. In oredr to avoid overwhelming the remote host and
+ # background. In order to avoid overwhelming the remote host and
# the local processor with too many ssh queries, the state is only
# requested every 'state_check_delay' seconds.
state_check_delay = 0.5
--- /dev/null
+#
+# 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 ResourceManager, clsinit_copy, ResourceState, \
+ reschedule_delay
+from nepi.resources.linux.application import LinuxApplication
+from nepi.util.timefuncs import tnow, tdiffsec
+
+import os
+import socket
+import time
+
+@clsinit_copy
+class UdpTunnel(LinuxApplication):
+ _rtype = "UdpTunnel"
+
+ def __init__(self, ec, guid):
+ super(UdpTunnel, self).__init__(ec, guid)
+ self._home = "udp-tunnel-%s" % self.guid
+ self._pid1 = None
+ self._ppid1 = None
+ self._pid2 = None
+ self._ppid2 = None
+
+ def log_message(self, msg):
+ return " guid %d - tunnel %s - %s - %s " % (self.guid,
+ self.endpoint1.node.get("hostname"),
+ self.endpoint2.node.get("hostname"),
+ msg)
+
+ def get_endpoints(self):
+ """ Returns the list of RM that are endpoints to the tunnel
+ """
+ connected = []
+ for guid in self.connections:
+ rm = self.ec.get_resource(guid)
+ if hasattr(rm, "udp_connect_command"):
+ connected.append(rm)
+ return connected
+
+ @property
+ def endpoint1(self):
+ endpoints = self.get_endpoints()
+ if endpoints: return endpoints[0]
+ return None
+
+ @property
+ def endpoint2(self):
+ endpoints = self.get_endpoints()
+ if endpoints and len(endpoints) > 1: return endpoints[1]
+ return None
+
+ def app_home(self, endpoint):
+ return os.path.join(endpoint.node.exp_home, self._home)
+
+ def run_home(self, endpoint):
+ return os.path.join(self.app_home(endpoint), self.ec.run_id)
+
+ def udp_connect(self, endpoint, remote_ip):
+ # Get udp connect command
+ local_port_file = os.path.join(self.run_home(endpoint),
+ "local_port")
+ remote_port_file = os.path.join(self.run_home(endpoint),
+ "remote_port")
+ ret_file = os.path.join(self.run_home(endpoint),
+ "ret_file")
+ udp_connect_command = endpoint.udp_connect_command(
+ remote_ip, local_port_file, remote_port_file,
+ ret_file)
+
+ # upload command to connect.sh script
+ shfile = os.path.join(self.app_home(endpoint), "udp-connect.sh")
+ endpoint.node.upload(udp_connect_command,
+ shfile,
+ text = True,
+ overwrite = False)
+
+ # invoke connect script
+ cmd = "bash %s" % shfile
+ (out, err), proc = endpoint.node.run(cmd, self.run_home(endpoint))
+
+ # check if execution errors occurred
+ msg = " Failed to connect endpoints "
+
+ if proc.poll():
+ self.fail()
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+
+ # Wait for pid file to be generated
+ pid, ppid = endpoint.node.wait_pid(self.run_home(endpoint))
+
+ # If the process is not running, check for error information
+ # on the remote machine
+ if not pid or not ppid:
+ (out, err), proc = endpoint.node.check_errors(self.run_home(endpoint))
+ # Out is what was written in the stderr file
+ if err:
+ self.fail()
+ msg = " Failed to start command '%s' " % command
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+
+ # wait until port is written to file
+ port = self.wait_local_port(endpoint)
+ return (port, pid, ppid)
+
+ def provision(self):
+ # create run dir for tunnel on each node
+ self.endpoint1.node.mkdir(self.run_home(self.endpoint1))
+ self.endpoint2.node.mkdir(self.run_home(self.endpoint2))
+
+ # Invoke connect script in endpoint 1
+ remote_ip1 = socket.gethostbyname(self.endpoint2.node.get("hostname"))
+ (port1, self._pid1, self._ppid1) = self.udp_connect(self.endpoint1,
+ remote_ip1)
+
+ # Invoke connect script in endpoint 2
+ remote_ip2 = socket.gethostbyname(self.endpoint1.node.get("hostname"))
+ (port2, self._pid2, self._ppid2) = self.udp_connect(self.endpoint2,
+ remote_ip2)
+
+ # upload file with port 2 to endpoint 1
+ self.upload_remote_port(self.endpoint1, port2)
+
+ # upload file with port 1 to endpoint 2
+ self.upload_remote_port(self.endpoint2, port1)
+
+ # check if connection was successful on both sides
+ self.wait_result(self.endpoint1)
+ self.wait_result(self.endpoint2)
+
+ self.info("Provisioning finished")
+
+ self.debug("----- READY ---- ")
+ self._provision_time = tnow()
+ self._state = ResourceState.PROVISIONED
+
+ def deploy(self):
+ if (not self.endpoint1 or self.endpoint1.state < ResourceState.READY) or \
+ (not self.endpoint2 or self.endpoint2.state < ResourceState.READY):
+ self.ec.schedule(reschedule_delay, self.deploy)
+ else:
+ try:
+ self.discover()
+ self.provision()
+ except:
+ self.fail()
+ raise
+
+ self.debug("----- READY ---- ")
+ self._ready_time = tnow()
+ self._state = ResourceState.READY
+
+ def start(self):
+ if self._state == ResourceState.READY:
+ command = self.get("command")
+ self.info("Starting command '%s'" % command)
+
+ self._start_time = tnow()
+ self._state = ResourceState.STARTED
+ else:
+ msg = " Failed to execute command '%s'" % command
+ self.error(msg, out, err)
+ self._state = ResourceState.FAILED
+ raise RuntimeError, msg
+
+ def stop(self):
+ command = self.get('command') or ''
+ state = self.state
+
+ if state == ResourceState.STARTED:
+ self.info("Stopping command '%s'" % command)
+
+ command = "bash %s" % os.path.join(self.app_home, "stop.sh")
+ (out, err), proc = self.execute_command(command,
+ blocking = True)
+
+ self._stop_time = tnow()
+ self._state = ResourceState.STOPPED
+
+ def stop(self):
+ """ Stops application execution
+ """
+ if self.state == ResourceState.STARTED:
+ stopped = True
+ self.info("Stopping tunnel")
+
+ # Only try to kill the process if the pid and ppid
+ # were retrieved
+ if self._pid1 and self._ppid1 and self._pid2 and self._ppid2:
+ (out1, err1), proc1 = self.endpoint1.node.kill(self._pid1,
+ self._ppid1, sudo = True)
+ (out2, err2), proc2 = self.endpoint2.node.kill(self._pid2,
+ self._ppid2, sudo = True)
+
+ if err1 or err2 or pro1.poll() or proc2.poll():
+ # check if execution errors occurred
+ msg = " Failed to STOP tunnel"
+ self.error(msg, out, err)
+ self.fail()
+ stopped = False
+
+ if stopped:
+ self._stop_time = tnow()
+ self._state = ResourceState.STOPPED
+
+ @property
+ def state(self):
+ """ Returns the state of the application
+ """
+ if self._state == ResourceState.STARTED:
+ # In order to avoid overwhelming the remote host and
+ # the local processor with too many ssh queries, the state is only
+ # requested every 'state_check_delay' seconds.
+ state_check_delay = 0.5
+ if tdiffsec(tnow(), self._last_state_check) > state_check_delay:
+ # check if execution errors occurred
+ (out1, err1), proc1 = self.endpoint1.node.check_errors(
+ self.run_home(self.endpoint1))
+
+ (out2, err2), proc2 = self.endpoint2.node.check_errors(
+ self.run_home(self.endpoint2))
+
+ if err1 or err2:
+ msg = " Failed to connect endpoints "
+ self.error(msg, err1, err2)
+ self.fail()
+
+ elif self._pid1 and self._ppid1 and self._pid2 and self._ppid2:
+ # No execution errors occurred. Make sure the background
+ # process with the recorded pid is still running.
+ status1 = self.node.status(self._pid1, self._ppid1)
+ status2 = self.node.status(self._pid2, self._ppid2)
+
+ if status1 == ProcStatus.FINISHED and \
+ satus2 == ProcStatus.FINISHED:
+ self._state = ResourceState.FINISHED
+
+ self._last_state_check = tnow()
+
+ return self._state
+
+ def wait_local_port(self, endpoint):
+ """ Waits until the local_port file for the endpoint is generated,
+ and returns the port number """
+ return self.wait_file(endpoint, "local_port")
+
+ def wait_result(self, endpoint):
+ """ Waits until the return code file for the endpoint is generated """
+ return self.wait_file(endpoint, "ret_file")
+
+ def wait_file(self, endpoint, filename):
+ """ Waits until file on endpoint is generated """
+ result = None
+ delay = 1.0
+
+ for i in xrange(4):
+ (out, err), proc = endpoint.node.check_output(
+ self.run_home(endpoint), filename)
+
+ if out:
+ result = out.strip()
+ break
+ else:
+ time.sleep(delay)
+ delay = delay * 1.5
+ else:
+ msg = "Couldn't retrieve %s" % filename
+ self.error(msg, out, err)
+ self.fail()
+ raise RuntimeError, msg
+
+ return result
+
+ def upload_remote_port(self, endpoint, port):
+ # upload remote port number to file
+ port = "%s\n" % port
+ endpoint.node.upload(port,
+ os.path.join(self.run_home(endpoint), "remote_port"),
+ text = True,
+ overwrite = False)
+
+ def valid_connection(self, guid):
+ # TODO: Validate!
+ return True
+
+++ /dev/null
-#
-# 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>
-
-import base64
-import errno
-import passfd
-import vsys
-import socket
-from optparse import OptionParser, SUPPRESS_HELP
-
-PASSFD_MSG = "PASSFD"
-
-def get_options():
- usage = ("usage: %prog -S <socket-name>")
-
- parser = OptionParser(usage = usage)
-
- parser.add_option("-S", "--socket-name", dest="socket_name",
- help = "Name for the unix socket used to interact with this process",
- default = "tap.sock", type="str")
-
- (options, args) = parser.parse_args()
-
- return (options.socket_name)
-
-if __name__ == '__main__':
-
- (socket_name) = get_options()
-
- # Socket to recive the file descriptor
- fdsock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
- fdsock.bind("")
- address = fdsock.getsockname()
-
- # vif-create-socket to send the PASSFD message
- 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)
-
- # Receive fd
- (fd, msg) = passfd.recvfd(fdsock)
-
- # Receive reply
- reply = sock.recv(1024)
- reply = base64.b64decode(reply)
-
- print reply, fd
-
-
-
-
--- /dev/null
+import base64
+import errno
+import os
+import passfd
+import signal
+import socket
+import time
+import tunchannel
+import vsys
+
+from optparse import OptionParser, SUPPRESS_HELP
+
+PASSFD_MSG = "PASSFD"
+
+# Trak SIGTERM, and set global termination flag instead of dying
+TERMINATE = []
+def _finalize(sig,frame):
+ global TERMINATE
+ TERMINATE.append(None)
+signal.signal(signal.SIGTERM, _finalize)
+
+# SIGUSR1 suspends forwading, SIGUSR2 resumes forwarding
+SUSPEND = []
+def _suspend(sig,frame):
+ global SUSPEND
+ if not SUSPEND:
+ SUSPEND.append(None)
+signal.signal(signal.SIGUSR1, _suspend)
+
+def _resume(sig,frame):
+ global SUSPEND
+ if SUSPEND:
+ SUSPEND.remove(None)
+signal.signal(signal.SIGUSR2, _resume)
+
+def get_fd(socket_name):
+ # Socket to recive the file descriptor
+ fdsock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ fdsock.bind("")
+ address = fdsock.getsockname()
+
+ # Socket to connect to the pl-vif-create process
+ # and send the PASSFD message
+ 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)
+
+ # Receive fd
+ (fd, msg) = passfd.recvfd(fdsock)
+
+ # Receive reply
+ reply = sock.recv(1024)
+ reply = base64.b64decode(reply)
+
+ sock.close()
+ fdsock.close()
+ return fd
+
+def get_options():
+ usage = ("usage: %prog -t <vif-type> -S <fd-socket-name> "
+ "-l <local-port-file> -r <remote-port-file> -H <remote-host> "
+ "-R <ret-file> ")
+
+ parser = OptionParser(usage = usage)
+
+ parser.add_option("-t", "--vif-type", dest="vif_type",
+ help = "Virtual interface type. Either IFF_TAP or IFF_TUN. "
+ "Defaults to IFF_TAP. ", type="str")
+ parser.add_option("-S", "--fd-socket-name", dest="fd_socket_name",
+ help = "Name for the unix socket to request the TAP file descriptor",
+ default = "tap.sock", 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")
+ parser.add_option("-R", "--ret-file", dest="ret_file",
+ help = "File where to store return code (success of connection) ",
+ default = "ret_file", type="str")
+
+ (options, args) = parser.parse_args()
+
+ vif_type = vsys.IFF_TAP
+ if options.vif_type and options.vif_type == "IFF_TUN":
+ vif_type = vsys.IFF_TUN
+
+ return ( vif_type, options.fd_socket_name, options.local_port_file,
+ options.remote_port_file, options.remote_host,
+ options.ret_file )
+
+if __name__ == '__main__':
+
+ ( vif_type, socket_name, local_port_file, remote_port_file,
+ remote_host, ret_file ) = get_options()
+
+ # Get the file descriptor of the TAP device from the process
+ # that created it
+ fd = get_fd(socket_name)
+ tun = os.fdopen(int(fd), 'r+b', 0)
+
+ # Create a local socket to stablish the tunnel connection
+ hostaddr = socket.gethostbyname(socket.gethostname())
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+ sock.bind((hostaddr, 0))
+ (local_host, local_port) = sock.getsockname()
+
+ # Save local port information to file
+ f = open(local_port_file, 'w')
+ f.write("%d\n" % local_port)
+ f.close()
+
+ # Wait until remote port information is available
+ while not os.path.exists(remote_port_file):
+ time.sleep(2)
+
+ # Read remote port from file
+ f = open(remote_port_file, 'r')
+ remote_port = f.read()
+ f.close()
+
+ remote_port = remote_port.strip()
+ remote_port = int(remote_port)
+
+ # Connect local socket to remote port
+ sock.connect((remote_host, remote_port))
+ remote = os.fdopen(sock.fileno(), 'r+b', 0)
+
+ # TODO: Test connectivity!
+
+ # Create a ret_file to indicate success
+ f = open(ret_file, 'w')
+ f.write("0")
+ f.close()
+
+ # Establish tunnel
+ # TODO: ADD parameters tunqueue, tunkqueue, cipher_key
+ tunchannel.tun_fwd(tun, remote,
+ # Planetlab TAP devices add PI headers
+ with_pi = True,
+ ether_mode = (vif_type == vsys.IFF_TAP),
+ cipher_key = None,
+ udp = True,
+ TERMINATE = TERMINATE,
+ SUSPEND = SUSPEND,
+ tunqueue = 1000,
+ tunkqueue = 500,
+ )
+
+
import time
# TODO: - routes!!!
+# - CREATE GRE - PlanetlabGRE - it only needs to set the gre and remote
+# properties when configuring the vif_up
PYTHON_VSYS_VERSION = "1.0"
"Name of the network interface (e.g. eth0, wlan0, etc)",
flags = Flags.ReadOnly)
- up = Attribute("up", "Link up", type = Types.Bool)
+ up = Attribute("up", "Link up",
+ type = Types.Bool)
- snat = Attribute("snat", "Set SNAT=1", type = Types.Bool,
- flags = Flags.ReadOnly)
+ snat = Attribute("snat", "Set SNAT=1",
+ type = Types.Bool,
+ flags = Flags.ExecReadOnly)
pointopoint = Attribute("pointopoint", "Peer IP address",
- flags = Flags.ReadOnly)
+ flags = Flags.ExecReadOnly)
tear_down = Attribute("tearDown", "Bash script to be executed before " + \
"releasing the resource",
"pl-vif-create.py")
self.node.upload(pl_vif_create,
- os.path.join(self.app_home, "pl-vif-create.py"),
+ os.path.join(self.node.src_dir, "pl-vif-create.py"),
overwrite = False)
# upload vif-stop python script
"pl-vif-stop.py")
self.node.upload(pl_vif_stop,
- os.path.join(self.app_home, "pl-vif-stop.py"),
+ os.path.join(self.node.src_dir, "pl-vif-stop.py"),
overwrite = False)
# upload vif-connect python script
pl_vif_connect = os.path.join(os.path.dirname(__file__), "scripts",
- "pl-vif-tunconnect.py")
+ "pl-vif-udp-connect.py")
self.node.upload(pl_vif_connect,
- os.path.join(self.app_home, "pl-vif-connect.py"),
+ os.path.join(self.node.src_dir, "pl-vif-udp-connect.py"),
overwrite = False)
# upload tun-connect python script
- tunchannel = os.path.join(os.path.dirname(__file__), "..", "all", "scripts",
- "tunchannel.py")
+ tunchannel = os.path.join(os.path.dirname(__file__), "..", "linux",
+ "scripts", "tunchannel.py")
self.node.upload(tunchannel,
- os.path.join(self.app_home, "tunchannel.py"),
+ os.path.join(self.node.src_dir, "tunchannel.py"),
overwrite = False)
# upload stop.sh script
self._run_in_background()
# Retrive if_name
- if_name = self.wait_if_name()
+ if_name = self._wait_if_name()
self.set("deviceName", if_name)
def deploy(self):
return self._state
- def wait_if_name(self):
+ def _wait_if_name(self):
""" Waits until the if_name file for the command is generated,
- and returns the if_name for the devide """
+ and returns the if_name for the device """
if_name = None
delay = 1.0
return if_name
+ def udp_connect_command(self, remote_ip, local_port_file,
+ remote_port_file, ret_file):
+ command = ["sudo -S "]
+ command.append("PYTHONPATH=$PYTHONPATH:${SRC}")
+ command.append("python ${SRC}/pl-vif-udp-connect.py")
+ command.append("-t %s" % self.vif_type)
+ command.append("-S %s " % self.sock_name)
+ command.append("-l %s " % local_port_file)
+ command.append("-r %s " % remote_port_file)
+ command.append("-H %s " % remote_ip)
+ command.append("-R %s " % ret_file)
+
+ command = " ".join(command)
+ command = self.replace_paths(command)
+ return command
+
@property
def _start_command(self):
- command = ["sudo -S python ${APP_HOME}/pl-vif-create.py"]
+ command = ["sudo -S python ${SRC}/pl-vif-create.py"]
command.append("-t %s" % self.vif_type)
command.append("-a %s" % self.get("ip4"))
@property
def _stop_command(self):
- command = ["sudo -S python ${APP_HOME}/pl-vif-stop.py"]
+ command = ["sudo -S python ${SRC}/pl-vif-stop.py"]
command.append("-S %s " % self.sock_name)
return " ".join(command)
@property
def _install(self):
+ # Install python-vsys and python-passfd
install_vsys = ( " ( "
" python -c 'import vsys, os; vsys.__version__ == \"%(version)s\" or os._exit(1)' "
" ) "
return wrapped
+def skipIfAnyNotAlive(func):
+ name = func.__name__
+ def wrapped(*args, **kwargs):
+ argss = list(args)
+ argss.pop(0)
+ username = argss.pop(0)
+
+ for hostname in argss:
+ node, ec = create_node(hostname, username)
+
+ if not node.is_alive():
+ print "*** WARNING: Skipping test %s: Node %s is not alive\n" % (
+ name, node.get("hostname"))
+ return
+
+ return func(*args, **kwargs)
+
+ return wrapped
+
def skipInteractive(func):
name = func.__name__
def wrapped(*args, **kwargs):
from nepi.execution.ec import ExperimentController
-from test_utils import skipIfNotAlive, skipInteractive
+from test_utils import skipIfNotAlive
import os
import time
from nepi.execution.ec import ExperimentController
-from test_utils import skipIfNotAlive, skipInteractive
+from test_utils import skipIfNotAlive
import os
import time
--- /dev/null
+#!/usr/bin/env python
+#
+# NEPI, a framework to manage network experiments
+# Copyright (C) 2013 INRIA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.execution.ec import ExperimentController
+
+from test_utils import skipIfAnyNotAlive
+
+import os
+import time
+import unittest
+
+class UdpTunnelTestCase(unittest.TestCase):
+ def setUp(self):
+ self.host1 = "nepi2.pl.sophia.inria.fr"
+ self.host2 = "nepi5.pl.sophia.inria.fr"
+ self.user = "inria_nepi"
+
+ @skipIfAnyNotAlive
+ def t_tap_udp_tunnel(self, user, host1, host2):
+
+ ec = ExperimentController(exp_id = "test-tap-udp-tunnel")
+
+ node1 = ec.register_resource("PlanetlabNode")
+ ec.set(node1, "hostname", host1)
+ ec.set(node1, "username", user)
+ ec.set(node1, "cleanHome", True)
+ ec.set(node1, "cleanProcesses", True)
+
+ tap1 = ec.register_resource("PlanetlabTap")
+ ec.set(tap1, "ip4", "192.168.1.1")
+ ec.set(tap1, "pointopoint", "192.168.1.2")
+ ec.set(tap1, "prefix4", 24)
+ ec.register_connection(tap1, node1)
+
+ node2 = ec.register_resource("PlanetlabNode")
+ ec.set(node2, "hostname", host2)
+ ec.set(node2, "username", user)
+ ec.set(node2, "cleanHome", True)
+ ec.set(node2, "cleanProcesses", True)
+
+ tap2 = ec.register_resource("PlanetlabTap")
+ ec.set(tap2, "ip4", "192.168.1.2")
+ ec.set(tap2, "pointopoint", "192.168.1.1")
+ ec.set(tap2, "prefix4", 24)
+ ec.register_connection(tap2, node2)
+
+ udptun = ec.register_resource("UdpTunnel")
+ ec.register_connection(tap1, udptun)
+ ec.register_connection(tap2, udptun)
+
+ app = ec.register_resource("LinuxApplication")
+ cmd = "ping -c3 192.168.1.2"
+ ec.set(app, "command", cmd)
+ ec.register_connection(app, node1)
+
+ ec.deploy()
+
+ ec.wait_finished(app)
+
+ ping = ec.trace(app, 'stdout')
+ expected = """3 packets transmitted, 3 received, 0% packet loss"""
+ self.assertTrue(ping.find(expected) > -1)
+
+ if_name = ec.get(tap1, "deviceName")
+ self.assertTrue(if_name.startswith("tap"))
+
+ if_name = ec.get(tap2, "deviceName")
+ self.assertTrue(if_name.startswith("tap"))
+
+
+ ec.shutdown()
+
+ def test_tap_udp_tunnel(self):
+ self.t_tap_udp_tunnel(self.user, self.host1, self.host2)
+
+if __name__ == '__main__':
+ unittest.main()
+