SRCDIR = $(CURDIR)/src
TESTDIR = $(CURDIR)/test
+TESTLIB = $(TESTDIR)/lib
BUILDDIR = $(CURDIR)/build
DISTDIR = $(CURDIR)/dist
BUILDDIR := $(BUILDDIR)/lib
endif
-PYPATH = $(BUILDDIR):$(PYTHONPATH)
+PYPATH = $(BUILDDIR):$(TESTLIB):$(PYTHONPATH)
COVERAGE = $(or $(shell which coverage), $(shell which python-coverage), \
coverage)
test: all
retval=0; \
for i in `find "$(TESTDIR)" -iname '*.py' -perm -u+x -type f`; do \
- echo $$i; \
+ @echo $$i; \
PYTHONPATH="$(PYPATH)" $$i -v || retval=$$?; \
done; exit $$retval
+test-one: all
+ @echo $(file) $(case)
+ PYTHONPATH="$(PYPATH)" python $(file) $(case)
+
coverage: all
rm -f .coverage
for i in `find "$(TESTDIR)" -perm -u+x -type f`; do \
newgrp.difference_update(intsec)
conditions[idx] = (newgrp, state, time)
- def get_connected(self, rtype = None):
+ def get_connected(self, rclass = None):
""" Returns the list of RM with the type 'rtype'
:param rtype: Type of the RM we look for
connected = []
for guid in self.connections:
rm = self.ec.get_resource(guid)
- if not rtype or rm.rtype() == rtype:
+ if not rclass or isinstance(rm, rclass):
connected.append(rm)
return connected
self._pid = None
self._ppid = None
self._home = "app-%s" % self.guid
+ # whether the command should run in foreground attached
+ # to a terminal
self._in_foreground = False
+ # whether to use sudo to kill the application process
+ self._sudo_kill = False
+
# keep a reference to the running process handler when
# the command is not executed as remote daemon in background
self._proc = None
@property
def node(self):
- node = self.get_connected(LinuxNode.rtype())
+ node = self.get_connected(LinuxNode)
if node: return node[0]
return None
if attr == TraceAttr.ALL:
(out, err), proc = self.node.check_output(self.run_home, name)
- if err and proc.poll():
+ if proc.poll():
msg = " Couldn't read trace %s " % name
self.error(msg, out, err)
return None
(out, err), proc = self.node.execute(cmd)
- if err and proc.poll():
+ if proc.poll():
msg = " Couldn't find trace %s " % name
self.error(msg, out, err)
return None
else:
if self.in_foreground:
- self._start_in_foreground()
+ self._run_in_foreground()
else:
- self._start_in_background()
+ self._run_in_background()
super(LinuxApplication, self).start()
- def _start_in_foreground(self):
+ def _run_in_foreground(self):
command = self.get("command")
sudo = self.get("sudo") or False
x11 = self.get("forwardX11")
# Command will be launched in foreground and attached to the
# terminal using the node 'execute' in non blocking mode.
- # Export environment
- env = self.get("env")
- environ = self.node.format_environment(env, inline = True)
- command = environ + command
- command = self.replace_paths(command)
-
# We save the reference to the process in self._proc
# to be able to kill the process from the stop method.
# We also set blocking = False, since we don't want the
# thread to block until the execution finishes.
- (out, err), self._proc = self.node.execute(command,
+ (out, err), self._proc = self.execute_command(self, command,
+ env = env,
sudo = sudo,
stdin = stdin,
forward_x11 = x11,
self.error(msg, out, err)
raise RuntimeError, msg
- def _start_in_background(self):
+ def _run_in_background(self):
command = self.get("command")
env = self.get("env")
sudo = self.get("sudo") or False
command = self.get('command') or ''
if self.state == ResourceState.STARTED:
+
stopped = True
self.info("Stopping command '%s'" % command)
# 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)
+ (out, err), proc = self.node.kill(self.pid, self.ppid, sudo =
+ self._sudo_kill)
if out or err:
# check if execution errors occurred
return self._state
+ def execute_command(self, command,
+ env = None,
+ sudo = False,
+ stdin = None,
+ forward_x11 = False,
+ blocking = False):
+
+ environ = ""
+ if env:
+ environ = self.node.format_environment(env, inline = True)
+ command = environ + command
+ command = self.replace_paths(command)
+
+ return self.node.execute(command,
+ sudo = sudo,
+ stdin = stdin,
+ forward_x11 = forward_x11,
+ blocking = blocking)
+
def replace_paths(self, command):
"""
Replace all special path tags with shell-escaped actual paths.
@property
def ccnd(self):
- ccnd = self.get_connected(LinuxCCND.rtype())
+ ccnd = self.get_connected(LinuxCCND)
if ccnd: return ccnd[0]
return None
def _environment(self):
return self.ccnd.path
- def execute_command(self, command, env):
- environ = self.node.format_environment(env, inline = True)
- command = environ + command
- command = self.replace_paths(command)
-
- return self.node.execute(command)
-
def valid_connection(self, guid):
# TODO: Validate!
return True
@property
def ccnr(self):
- ccnr = self.get_connected(LinuxCCNR.rtype())
+ ccnr = self.get_connected(LinuxCCNR)
if ccnr: return ccnr[0]
return None
def _environment(self):
return self.ccnd.path
- def execute_command(self, command, env):
- environ = self.node.format_environment(env, inline = True)
- command = environ + command
- command = self.replace_paths(command)
-
- return self.node.execute(command)
-
def valid_connection(self, guid):
# TODO: Validate!
return True
@property
def ccnd(self):
- ccnd = self.get_connected(LinuxCCND.rtype())
+ ccnd = self.get_connected(LinuxCCND)
if ccnd: return ccnd[0]
return None
@property
def ccnd(self):
- ccnd = self.get_connected(LinuxCCND.rtype())
+ ccnd = self.get_connected(LinuxCCND)
if ccnd: return ccnd[0]
return None
def _environment(self):
return self.ccnd.path
- def execute_command(self, command, env):
- environ = self.node.format_environment(env, inline = True)
- command = environ + command
- command = self.replace_paths(command)
-
- return self.node.execute(command)
-
def valid_connection(self, guid):
# TODO: Validate!
return True
@property
def node(self):
- node = self.get_connected(LinuxNode.rtype())
+ node = self.get_connected(LinuxNode)
if node: return node[0]
return None
@property
def channel(self):
- chan = self.get_connected(LinuxChannel.rtype())
+ chan = self.get_connected(LinuxChannel)
if chan: return chan[0]
return None
# Node needs to wait until all associated interfaces are
# ready before it can finalize deployment
from nepi.resources.linux.interface import LinuxInterface
- ifaces = self.get_connected(LinuxInterface.rtype())
+ ifaces = self.get_connected(LinuxInterface)
for iface in ifaces:
if iface.state < ResourceState.READY:
self.ec.schedule(reschedule_delay, self.deploy)
return self.upload(command, shfile, text = True, overwrite = overwrite)
def format_environment(self, env, inline = False):
- """Format environmental variables for command to be executed either
- as an inline command
+ """ 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
as a bash script (i.e. export PYTHONPATH=src/.. \n export LALA=.. \n)
"""
def check_errors(self, home,
ecodefile = "exitcode",
stderr = "stderr"):
- """
- Checks whether errors occurred while running a command.
+ """ Checks whether errors occurred while running a command.
It first checks the exit code for the command, and only if the
exit code is an error one it returns the error output.
from nepi.execution.resource import ResourceManager, clsinit, ResourceState, \
reschedule_delay
from nepi.execution.attribute import Attribute, Flags
+from nepi.resources.omf.node import OMFNode
from nepi.resources.omf.omf_api import OMFAPIFactory
self.get('appid') + " : " + self.get('path') + " : " + \
self.get('args') + " : " + self.get('env')
self.info(msg)
- rm_list = self.get_connected("OMFNode")
+ rm_list = self.get_connected(OMFNode)
try:
for rm_node in rm_list:
if rm_node.get('hostname') :
reschedule_delay
from nepi.execution.attribute import Attribute, Flags
+from nepi.resources.omf.node import OMFNode
from nepi.resources.omf.omf_api import OMFAPIFactory
self.debug(" " + self.rtype() + " ( Guid : " + str(self._guid) +") : " + \
self.get('mode') + " : " + self.get('type') + " : " + \
self.get('essid') + " : " + self.get('ip'))
- rm_list = self.get_connected("OMFNode")
+ rm_list = self.get_connected(OMFNode)
for rm_node in rm_list:
if rm_node.state < ResourceState.READY:
self.ec.schedule(reschedule_delay, self.deploy)
@classmethod
def _register_attributes(cls):
- cls._remove_attribute("username")
-
ip = Attribute("ip", "PlanetLab host public IP address",
flags = Flags.ReadOnly)
- slicename = Attribute("slice", "PlanetLab slice name",
- flags = Flags.Credential)
-
pl_url = Attribute("plcApiUrl", "URL of PlanetLab PLCAPI host (e.g. www.planet-lab.eu or www.planet-lab.org) ",
default = "www.planet-lab.eu",
flags = Flags.Credential)
flags = Flags.Filter)
cls._register_attribute(ip)
- cls._register_attribute(slicename)
cls._register_attribute(pl_url)
cls._register_attribute(pl_ptn)
cls._register_attribute(city)
cls._register_attribute(timeframe)
def __init__(self, ec, guid):
- super(PLanetlabNode, self).__init__(ec, guid)
+ super(PlanetlabNode, self).__init__(ec, guid)
self._plapi = None
@property
def plapi(self):
if not self._plapi:
- slicename = self.get("slice")
+ slicename = self.get("username")
pl_pass = self.get("password")
pl_url = self.get("plcApiUrl")
pl_ptn = self.get("plcApiPattern")
return self._plapi
- @property
- def os(self):
- if self._os:
- return self._os
-
- if (not self.get("hostname") or not self.get("username")):
- msg = "Can't resolve OS, insufficient data "
- self.error(msg)
- raise RuntimeError, msg
-
- (out, err), proc = self.execute("cat /etc/issue", with_lock = True)
-
- if err and proc.poll():
- msg = "Error detecting OS "
- self.error(msg, out, err)
- raise RuntimeError, "%s - %s - %s" %( msg, out, err )
-
- if out.find("Fedora release 12") == 0:
- self._os = "f12"
- elif out.find("Fedora release 14") == 0:
- self._os = "f14"
- else:
- msg = "Unsupported OS"
- self.error(msg, out)
- raise RuntimeError, "%s - %s " %( msg, out )
-
- return self._os
-
- def provision(self):
- if not self.is_alive():
- self._state = ResourceState.FAILED
- msg = "Deploy failed. Unresponsive node %s" % self.get("hostname")
- self.error(msg)
- raise RuntimeError, msg
-
- if self.get("cleanProcesses"):
- self.clean_processes()
-
- if self.get("cleanHome"):
- self.clean_home()
-
- self.mkdir(self.node_home)
-
- super(PlanetlabNode, self).provision()
-
- def deploy(self):
- if self.state == ResourceState.NEW:
- try:
- self.discover()
- if self.state == ResourceState.DISCOVERED:
- self.provision()
- except:
- self._state = ResourceState.FAILED
- raise
-
- if self.state != ResourceState.PROVISIONED:
- self.ec.schedule(reschedule_delay, self.deploy)
-
- super(PlanetlabNode, self).deploy()
-
def valid_connection(self, guid):
# TODO: Validate!
return True
- def clean_processes(self, killer = False):
- self.info("Cleaning up processes")
-
- # Hardcore kill
- cmd = ("sudo -S killall python tcpdump || /bin/true ; " +
- "sudo -S killall python tcpdump || /bin/true ; " +
- "sudo -S kill $(ps -N -T -o pid --no-heading | grep -v $PPID | sort) || /bin/true ; " +
- "sudo -S killall -u root || /bin/true ; " +
- "sudo -S killall -u root || /bin/true ; ")
-
- out = err = ""
- (out, err), proc = self.execute(cmd, retry = 1, with_lock = True)
-
def blacklist(self):
# TODO!!!!
self.warn(" Blacklisting malfunctioning node ")
--- /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.resources.planetlab.node import PlanetlabNode
+from nepi.util.timefuncs import tnow, tdiffsec
+
+import os
+import time
+
+# TODO: - routes!!!
+# - Make base clase 'virtual device' and redefine vif_type
+# - write the name of the device (if_name) to a file and allow the
+# RM to read it and set the 'deviceName' attribute
+# - Instead of doing an infinite loop, open a port for communication allowing
+# to pass the fd to another process
+
+PYTHON_VSYS_VERSION = "1.0"
+
+@clsinit_copy
+class PlanetlabTap(LinuxApplication):
+ _rtype = "PlanetlabTap"
+
+ @classmethod
+ def _register_attributes(cls):
+ ip4 = Attribute("ip4", "IPv4 Address",
+ flags = Flags.ExecReadOnly)
+
+ mac = Attribute("mac", "MAC Address",
+ flags = Flags.ExecReadOnly)
+
+ prefix4 = Attribute("prefix4", "IPv4 network prefix",
+ flags = Flags.ExecReadOnly)
+
+ mtu = Attribute("mtu", "Maximum transmition unit for device",
+ type = Types.Integer)
+
+ devname = Attribute("deviceName",
+ "Name of the network interface (e.g. eth0, wlan0, etc)",
+ flags = Flags.ReadOnly)
+
+ up = Attribute("up", "Link up", type = Types.Bool)
+
+ snat = Attribute("snat", "Set SNAT=1", type = Types.Bool,
+ flags = Flags.ReadOnly)
+
+ pointopoint = Attribute("pointopoint", "Peer IP address",
+ flags = Flags.ReadOnly)
+
+ tear_down = Attribute("tearDown", "Bash script to be executed before " + \
+ "releasing the resource",
+ flags = Flags.ExecReadOnly)
+
+ cls._register_attribute(ip4)
+ cls._register_attribute(mac)
+ cls._register_attribute(prefix4)
+ cls._register_attribute(mtu)
+ cls._register_attribute(devname)
+ cls._register_attribute(up)
+ cls._register_attribute(snat)
+ cls._register_attribute(pointopoint)
+ cls._register_attribute(tear_down)
+
+ def __init__(self, ec, guid):
+ super(PlanetlabTap, self).__init__(ec, guid)
+ self._home = "tap-%s" % self.guid
+
+ @property
+ def node(self):
+ node = self.get_connected(PlanetlabNode)
+ if node: return node[0]
+ return None
+
+ def upload_sources(self):
+ depends = "mercurial make gcc"
+ self.set("depends", depends)
+
+ install = ( " ( "
+ " python -c 'import vsys, os; vsys.__version__ == \"%(version)s\" or os._exit(1)' "
+ " ) "
+ " ||"
+ " ( "
+ " cd ${SRC} ; "
+ " hg clone http://nepi.inria.fr/code/python-vsys ; "
+ " cd python-vsys ; "
+ " make all ; "
+ " sudo -S make install "
+ " )" ) % ({
+ "version": PYTHON_VSYS_VERSION
+ })
+
+ self.set("install", install)
+
+ def upload_start_command(self):
+ # upload tap-creation python script
+ start_script = self.replace_paths(self._start_script)
+ self.node.upload(start_script,
+ os.path.join(self.app_home, "tap_create.py"),
+ text = True,
+ overwrite = False)
+
+ # upload start.sh
+ start_command = self.replace_paths(self._start_command)
+
+ self.info("Uploading command '%s'" % start_command)
+
+ self.set("command", start_command)
+ self.node.upload(start_command,
+ os.path.join(self.app_home, "start.sh"),
+ text = True,
+ overwrite = False)
+
+ # We want to make sure the device is up and 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 )
+ self._run_in_background()
+
+ # Retrive if_name
+ if_name = self.wait_if_name()
+ self.set("deviceName", if_name)
+
+ def deploy(self):
+ if not self.node or self.node.state < ResourceState.PROVISIONED:
+ 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 = "rm %s" % os.path.join(self.run_home, "if_stop")
+ (out, err), proc = self.execute_command(command)
+
+ self._stop_time = tnow()
+ self._state = ResourceState.STOPPED
+
+ @property
+ def state(self):
+ # First check if the ccnd has failed
+ state_check_delay = 0.5
+ if self._state == ResourceState.STARTED and \
+ tdiffsec(tnow(), self._last_state_check) > state_check_delay:
+
+ if self.get("deviceName"):
+ (out, err), proc = self.node.execute("ifconfig")
+
+ if out.strip().find(self.get("deviceName")) == -1:
+ # tap is not running is not running (socket not found)
+ self._state = ResourceState.FINISHED
+
+ self._last_state_check = tnow()
+
+ return self._state
+
+ def wait_if_name(self):
+ """ Waits until the if_name file for the command is generated,
+ and returns the if_name for the devide """
+ if_name = None
+ delay = 1.0
+
+ for i in xrange(4):
+ (out, err), proc = self.node.check_output(self.run_home, "if_name")
+
+ if out:
+ if_name = out.strip()
+ break
+ else:
+ time.sleep(delay)
+ delay = delay * 1.5
+ else:
+ msg = "Couldn't retrieve if_name"
+ self.error(msg, out, err)
+ self.fail()
+ raise RuntimeError, msg
+
+ return if_name
+
+ @property
+ def _start_command(self):
+ return "sudo -S python ${APP_HOME}/tap_create.py"
+
+ @property
+ def _start_script(self):
+ return ( "import vsys, time, os \n"
+ "(fd, if_name) = vsys.fd_tuntap(vsys.%(devtype)s)\n"
+ "vsys.vif_up(if_name, '%(ip)s', %(prefix)s%(snat)s%(pointopoint)s)\n"
+ "f = open('%(if_name_file)s', 'w')\n"
+ "f.write(if_name)\n"
+ "f.close()\n\n"
+ "f = open('%(if_stop_file)s', 'w')\n"
+ "f.close()\n\n"
+ "while os.path.exists('%(if_stop_file)s'):\n"
+ " time.sleep(2)\n"
+ ) % ({
+ "devtype": self._vif_type,
+ "ip": self.get("ip4"),
+ "prefix": self.get("prefix4"),
+ "snat": ", snat=True" if self.get("snat") else "",
+ "pointopoint": ", pointopoint=%s" % self.get("pointopoint") \
+ if self.get("pointopoint") else "",
+ "if_name_file": os.path.join(self.run_home, "if_name"),
+ "if_stop_file": os.path.join(self.run_home, "if_stop"),
+ })
+
+ @property
+ def _vif_type(self):
+ return "IFF_TAP"
+
+ 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>
+
+from nepi.execution.resource import clsinit_copy
+from nepi.resources.planetlab.tap import PlanetlabTap
+
+@clsinit_copy
+class PlanetlabTun(PlanetlabTap):
+ _rtype = "PlanetlabTun"
+
+ def __init__(self, ec, guid):
+ super(PlanetlabTun, self).__init__(ec, guid)
+ self._home = "tun-%s" % self.guid
+
+ @property
+ def _vif_type(self):
+ return "IFF_TUN"
+
+
super(Interface, self).__init__(ec, guid)
def deploy(self):
- node = self.get_connected(Node.rtype())[0]
- chan = self.get_connected(Channel.rtype())[0]
+ node = self.get_connected(Node)[0]
+ chan = self.get_connected(Channel)[0]
if node.state < ResourceState.PROVISIONED:
self.ec.schedule("0.5s", self.deploy)
self.logger.debug(" -------- PROVISIONED ------- ")
self.ec.schedule("3s", self.deploy)
elif self.state == ResourceState.PROVISIONED:
- ifaces = self.get_connected(Interface.rtype())
+ ifaces = self.get_connected(Interface)
for rm in ifaces:
if rm.state < ResourceState.READY:
self.ec.schedule("0.5s", self.deploy)
super(Application, self).__init__(ec, guid)
def deploy(self):
- node = self.get_connected(Node.rtype())[0]
+ node = self.get_connected(Node)[0]
if node.state < ResourceState.READY:
self.ec.schedule("0.5s", self.deploy)
else:
self.host1 = 'nepi2.pl.sophia.inria.fr'
self.host2 = 'nepi5.pl.sophia.inria.fr'
-
def test_list_hosts(self):
slicename = os.environ.get('PL_USER')
pl_pass = os.environ.get('PL_PASS')
--- /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 nepi.resources.planetlab.node import PlanetlabNode
+from nepi.resources.planetlab.tap import PlanetlabTap
+from nepi.resources.linux.application import LinuxApplication
+
+from test_utils import skipIfNotAlive, skipInteractive
+
+import os
+import time
+import unittest
+
+class PlanetlabTapTestCase(unittest.TestCase):
+ def setUp(self):
+ self.host = "nepi2.pl.sophia.inria.fr"
+ self.user = "inria_nepi"
+
+ @skipIfNotAlive
+ def t_tap_create(self, host, user):
+ from nepi.execution.resource import ResourceFactory
+
+ ResourceFactory.register_type(PlanetlabNode)
+ ResourceFactory.register_type(PlanetlabTap)
+ ResourceFactory.register_type(LinuxApplication)
+
+ ec = ExperimentController(exp_id = "test-tap-create")
+
+ node = ec.register_resource("PlanetlabNode")
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "cleanHome", True)
+ ec.set(node, "cleanProcesses", True)
+
+ tap = ec.register_resource("PlanetlabTap")
+ ec.set(tap, "ip4", "192.168.1.1")
+ ec.set(tap, "prefix4", "24")
+ ec.register_connection(tap, node)
+
+ app = ec.register_resource("LinuxApplication")
+ cmd = "ping -c3 192.168.1.1"
+ ec.set(app, "command", cmd)
+ ec.register_connection(app, node)
+
+ 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(tap, "deviceName")
+ self.assertTrue(if_name.startswith("tap"))
+
+ ec.shutdown()
+
+ def test_tap_create(self):
+ self.t_tap_create(self.host, self.user)
+
+if __name__ == '__main__':
+ unittest.main()
+
--- /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 nepi.resources.planetlab.node import PlanetlabNode
+from nepi.resources.planetlab.tun import PlanetlabTun
+from nepi.resources.linux.application import LinuxApplication
+
+from test_utils import skipIfNotAlive, skipInteractive
+
+import os
+import time
+import unittest
+
+class PlanetlabTunTestCase(unittest.TestCase):
+ def setUp(self):
+ self.host = "nepi2.pl.sophia.inria.fr"
+ self.user = "inria_nepi"
+
+ @skipIfNotAlive
+ def t_tun_create(self, host, user):
+ from nepi.execution.resource import ResourceFactory
+
+ ResourceFactory.register_type(PlanetlabNode)
+ ResourceFactory.register_type(PlanetlabTun)
+ ResourceFactory.register_type(LinuxApplication)
+
+ ec = ExperimentController(exp_id = "test-un-create")
+
+ node = ec.register_resource("PlanetlabNode")
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "cleanHome", True)
+ ec.set(node, "cleanProcesses", True)
+
+ tun = ec.register_resource("PlanetlabTun")
+ ec.set(tun, "ip4", "192.168.1.1")
+ ec.set(tun, "prefix4", "24")
+ ec.register_connection(tun, node)
+
+ app = ec.register_resource("LinuxApplication")
+ cmd = "ping -c3 192.168.1.1"
+ ec.set(app, "command", cmd)
+ ec.register_connection(app, node)
+
+ 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(tun, "deviceName")
+ self.assertTrue(if_name.startswith("tun"))
+
+ ec.shutdown()
+
+ def test_tun_create(self):
+ self.t_tun_create(self.host, self.user)
+
+if __name__ == '__main__':
+ unittest.main()
+