"nepi.resources.linux.ccn",
"nepi.resources.linux.ns3",
"nepi.resources.linux.ns3.ccn",
+ "nepi.resources.linux.netns",
"nepi.resources.netns",
"nepi.resources.ns3",
"nepi.resources.ns3.classes",
--- /dev/null
+#
+# 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>
+
+import base64
+import cPickle
+import errno
+import os
+import socket
+import time
+import weakref
+
+from optparse import OptionParser, SUPPRESS_HELP
+
+from nepi.resources.netns.netnsclient import NetNSClient
+from nepi.resources.netns.netnsserver import NetNSWrapperMessage
+
+class LinuxNetNSClient(NetNSClient):
+ def __init__(self, emulation):
+ super(LinuxNetNSClient, self).__init__()
+ self._emulation = weakref.ref(emulation)
+
+ self._socat_proc = None
+
+ @property
+ def emulation(self):
+ return self._emulation()
+
+ def send_msg(self, msg_type, *args, **kwargs):
+ msg = [msg_type, args, kwargs]
+
+ def encode(item):
+ item = cPickle.dumps(item)
+ return base64.b64encode(item)
+
+ encoded = "|".join(map(encode, msg))
+
+ if self.emulation.node.get("hostname") in ['localhost', '127.0.0.1']:
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(self.emulation.remote_socket)
+ sock.send("%s\n" % encoded)
+ reply = sock.recv(1024)
+ sock.close()
+ else:
+ command = ( "python -c 'import socket;"
+ "sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM);"
+ "sock.connect(\"%(socket_addr)s\");"
+ "msg = \"%(encoded_message)s\\n\";"
+ "sock.send(msg);"
+ "reply = sock.recv(1024);"
+ "sock.close();"
+ "print reply'") % {
+ "encoded_message": encoded,
+ "socket_addr": self.emulation.remote_socket,
+ }
+
+ (reply, err), proc = self.emulation.node.execute(command,
+ with_lock = True)
+
+ if (err and proc.poll()) or reply.strip() == "":
+ msg = (" Couldn't connect to remote socket %s - REPLY: %s "
+ "- ERROR: %s ") % (
+ self.emulation.remote_socket, reply, err)
+ self.emulation.error(msg, reply, err)
+ raise RuntimeError(msg)
+
+ reply = cPickle.loads(base64.b64decode(reply))
+
+ return reply
+
+ def create(self, *args, **kwargs):
+ return self.send_msg(NetNSWrapperMessage.CREATE, *args, **kwargs)
+
+ def invoke(self, *args, **kwargs):
+ return self.send_msg(NetNSWrapperMessage.INVOKE, *args, **kwargs)
+
+ def set(self, *args, **kwargs):
+ return self.send_msg(NetNSWrapperMessage.SET, *args, **kwargs)
+
+ def get(self, *args, **kwargs):
+ return self.send_msg(NetNSWrapperMessage.GET, *args, **kwargs)
+
+ def flush(self, *args, **kwargs):
+ return self.send_msg(NetNSWrapperMessage.FLUSH, *args, **kwargs)
+
+ def shutdown(self, *args, **kwargs):
+ try:
+ return self.send_msg(NetNSWrapperMessage.SHUTDOWN, *args, **kwargs)
+ except:
+ pass
+
+ return None
+
--- /dev/null
+#
+# 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.trace import Trace, TraceAttr
+from nepi.execution.resource import ResourceManager, clsinit_copy, \
+ ResourceState, ResourceFactory, reschedule_delay
+from nepi.resources.linux.application import LinuxApplication
+from nepi.util.timefuncs import tnow, tdiffsec
+from nepi.resources.netns.netnsemulation import NetNSEmulation
+from nepi.resources.linux.netns.netnsclient import LinuxNetNSClient
+
+import os
+import time
+import threading
+
+@clsinit_copy
+class LinuxNetNSEmulation(LinuxApplication, NetNSEmulation):
+ _rtype = "LinuxNetNSEmulation"
+
+ @classmethod
+ def _register_attributes(cls):
+ verbose = Attribute("verbose",
+ "True to output debugging info for the client-server communication",
+ type = Types.Bool,
+ flags = Flags.Design)
+
+ enable_dump = Attribute("enableDump",
+ "Enable dumping the remote executed commands to a script "
+ "in order to later reproduce and debug the experiment",
+ type = Types.Bool,
+ default = False,
+ flags = Flags.Design)
+
+ version = Attribute("version",
+ "Version of netns to install from nsam repo",
+ default = "netns-dev",
+ flags = Flags.Design)
+
+ cls._register_attribute(enable_dump)
+ cls._register_attribute(verbose)
+ cls._register_attribute(version)
+
+ def __init__(self, ec, guid):
+ LinuxApplication.__init__(self, ec, guid)
+ NetNSEmulation.__init__(self)
+
+ self._client = None
+ self._home = "netns-emu-%s" % self.guid
+ self._socket_name = "netns-%s.sock" % os.urandom(4).encode('hex')
+
+ @property
+ def socket_name(self):
+ return self._socket_name
+
+ @property
+ def remote_socket(self):
+ return os.path.join(self.run_home, self.socket_name)
+
+ def upload_sources(self):
+ self.node.mkdir(os.path.join(self.node.src_dir, "netnswrapper"))
+
+ # upload wrapper python script
+ wrapper = os.path.join(os.path.dirname(__file__), "..", "..", "netns",
+ "netnswrapper.py")
+
+ self.node.upload(wrapper,
+ os.path.join(self.node.src_dir, "netnswrapper", "netnswrapper.py"),
+ overwrite = False)
+
+ # upload wrapper debug python script
+ wrapper_debug = os.path.join(os.path.dirname(__file__), "..", "..", "netns",
+ "netnswrapper_debug.py")
+
+ self.node.upload(wrapper_debug,
+ os.path.join(self.node.src_dir, "netnswrapper", "netnswrapper_debug.py"),
+ overwrite = False)
+
+ # upload server python script
+ server = os.path.join(os.path.dirname(__file__), "..", "..", "netns",
+ "netnsserver.py")
+
+ self.node.upload(server,
+ os.path.join(self.node.src_dir, "netnswrapper", "netnsserver.py"),
+ overwrite = False)
+
+ # Upload user defined sources
+ self.node.mkdir(os.path.join(self.node.src_dir, "netns"))
+ src_dir = os.path.join(self.node.src_dir, "netns")
+
+ super(LinuxNetNSEmulation, self).upload_sources(src_dir = src_dir)
+
+ def upload_extra_sources(self, sources = None, src_dir = None):
+ return super(LinuxNetNSEmulation, self).upload_sources(
+ sources = sources,
+ src_dir = src_dir)
+
+ def upload_start_command(self):
+ command = self.get("command")
+ env = self.get("env")
+
+ # We want to make sure the emulator is running
+ # before the experiment starts.
+ # Run the command as a bash script in background,
+ # in the host ( but wait until the command has
+ # finished to continue )
+ env = self.replace_paths(env)
+ command = self.replace_paths(command)
+
+ shfile = os.path.join(self.app_home, "start.sh")
+ self.node.upload_command(command,
+ shfile = shfile,
+ env = env,
+ overwrite = True)
+
+ # Run the wrapper
+ self._run_in_background()
+
+ # Wait until the remote socket is created
+ self.wait_remote_socket()
+
+ def do_deploy(self):
+ if not self.node or self.node.state < ResourceState.READY:
+ self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.node.state )
+
+ # ccnd needs to wait until node is deployed and running
+ self.ec.schedule(reschedule_delay, self.deploy)
+ else:
+ if not self.get("command"):
+ self.set("command", self._start_command)
+
+ if not self.get("depends"):
+ self.set("depends", self._dependencies)
+
+ if self.get("sources"):
+ sources = self.get("sources")
+ source = sources.split(" ")[0]
+ basename = os.path.basename(source)
+ version = ( basename.strip().replace(".tar.gz", "")
+ .replace(".tar","")
+ .replace(".gz","")
+ .replace(".zip","") )
+
+ self.set("version", version)
+ self.set("sources", source)
+
+ if not self.get("build"):
+ self.set("build", self._build)
+
+ if not self.get("env"):
+ self.set("env", self._environment)
+
+ self.do_discover()
+ self.do_provision()
+
+ # Create client
+ self._client = LinuxNetNSClient(self)
+
+ self.set_ready()
+
+ def do_release(self):
+ self.info("Releasing resource")
+
+ tear_down = self.get("tearDown")
+ if tear_down:
+ self.node.execute(tear_down)
+
+ self.do_stop()
+ self._client.shutdown()
+ LinuxApplication.do_stop(self)
+
+ super(LinuxApplication, self).do_release()
+
+ @property
+ def _start_command(self):
+ command = []
+
+ #command.append("sudo")
+ command.append("PYTHONPATH=$PYTHONPATH:${SRC}/netnswrapper/")
+ command.append("python ${SRC}/netnswrapper/netnsserver.py -S %s" % \
+ os.path.basename(self.remote_socket) )
+
+ if self.get("enableDump"):
+ command.append("-D")
+
+ if self.get("verbose"):
+ command.append("-v")
+
+ command = " ".join(command)
+ return command
+
+ @property
+ def _dependencies(self):
+ if self.node.use_rpm:
+ return ( " python python-devel mercurial unzip bridge-utils iproute")
+ elif self.node.use_deb:
+ return ( " python python-dev mercurial unzip bridge-utils iproute")
+ return ""
+
+ @property
+ def netns_repo(self):
+ return "http://nepi.inria.fr/code/netns"
+
+ @property
+ def netns_version(self):
+ version = self.get("version")
+ return version or "dev"
+
+ @property
+ def python_unshare_repo(self):
+ return "http://nepi.inria.fr/code/python-unshare"
+
+ @property
+ def python_unshare_version(self):
+ return "dev"
+
+ @property
+ def python_passfd_repo(self):
+ return "http://nepi.inria.fr/code/python-passfd"
+
+ @property
+ def python_passfd_version(self):
+ return "dev"
+
+ @property
+ def netns_src(self):
+ location = "${SRC}/netns/%(version)s" \
+ % {
+ "version": self.netns_version,
+ }
+
+ return location
+
+ @property
+ def python_unshare_src(self):
+ location = "${SRC}/python_unshare/%(version)s" \
+ % {
+ "version": self.python_unshare_version,
+ }
+
+ return location
+
+ @property
+ def python_passfd_src(self):
+ location = "${SRC}/python_passfd/%(version)s" \
+ % {
+ "version": self.python_passfd_version,
+ }
+
+ return location
+
+ def clone_command(self, name, repo, src):
+ clone_cmd = (
+ # Test if alredy cloned
+ " ( "
+ " ( "
+ " ( test -d %(src)s ) "
+ " && echo '%(name)s binaries found, nothing to do'"
+ " ) "
+ " ) "
+ " || "
+ # clone source code
+ " ( "
+ " mkdir -p %(src)s && "
+ " hg clone %(repo)s %(src)s"
+ " ) "
+ ) % {
+ "repo": repo,
+ "src": src,
+ "name": name,
+ }
+
+ return clone_cmd
+
+ @property
+ def _build(self):
+ netns_clone = self.clone_command("netns", self.netns_repo,
+ self.netns_src)
+ python_unshare_clone = self.clone_command("python_unshare",
+ self.python_unshare_repo, self.python_unshare_src)
+ python_passfd_clone = self.clone_command("python_passfd",
+ self.python_passfd_repo, self.python_passfd_src)
+
+ build_cmd = (
+ # Netns installation
+ "( %(netns_clone)s )"
+ " && "
+ "( %(python_unshare_clone)s )"
+ " && "
+ "( %(python_passfd_clone)s )"
+ ) % {
+ "netns_clone": netns_clone,
+ "python_unshare_clone": python_unshare_clone,
+ "python_passfd_clone": python_passfd_clone,
+ }
+
+ return build_cmd
+
+ @property
+ def _environment(self):
+ env = []
+ env.append("PYTHONPATH=$PYTHONPAH:%(netns_src)s/src/:%(python_unshare_src)s/src:%(python_passfd_src)s/src}" % {
+ "netns_src": self.netns_src,
+ "python_unshare_src": self.python_unshare_src,
+ "python_passfd_src": self.python_passfd_src,
+ })
+
+ 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)
+ )
+
+ def valid_connection(self, guid):
+ # TODO: Validate!
+ return True
+
+ def wait_remote_socket(self):
+ """ Waits until the remote socket is created
+ """
+ command = " [ -e %s ] && echo 'DONE' " % self.remote_socket
+
+ for i in xrange(200):
+ (out, err), proc = self.node.execute(command, retry = 1,
+ with_lock = True)
+
+ if out.find("DONE") > -1:
+ break
+ else:
+ raise RuntimeError("Remote socket not found at %s" % \
+ self.remote_socket)
+
+
--- /dev/null
+#
+# 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
+from nepi.resources.netns.netnsbase import NetNSBase
+from nepi.execution.resource import clsinit_copy, ResourceState, \
+ reschedule_delay
+
+import shlex
+
+@clsinit_copy
+class NetNSApplication(NetNSBase):
+ _rtype = "netns::Application"
+
+ def __init__(self, ec, guid):
+ super(NetNSApplication, self).__init__(ec, guid)
+ self._traces = dict()
+
+ @classmethod
+ def _register_attributes(cls):
+ command = Attribute("command", "Command to execute", flags=Flags.Design)
+ cls._register_attribute(command)
+
+ @property
+ def emulation(self):
+ return self.node.emulation
+
+ @property
+ def node(self):
+ from nepi.resources.netns.netnsnode import NetNSNode
+ node = self.get_connected(NetNSNode.get_rtype())
+
+ if not node:
+ msg = "Route not connected to Node!!"
+ self.error(msg)
+ raise RuntimeError, msg
+
+ return node[0]
+
+ @property
+ def _rms_to_wait(self):
+ return [self.node]
+
+ def do_start(self):
+ if self.simulation.state < ResourceState.STARTED:
+ self.debug("---- RESCHEDULING START ----" )
+ self.ec.schedule(reschedule_delay, self.start)
+ else:
+ self._configure_traces()
+
+ command = shlex.split(self.get("command"))
+ stdout = self._traces["stdout"]
+ stderr = self._traces["stderr"]
+ self._uuid = self.emulation.invoke(self.node.uuid,
+ "Popen", command, stdout = stdout,
+ stderr = stderr)
+
+ super(NetNSApplication, self).do_start()
+ self._start_time = self.emulation.start_time
+
+ def _configure_traces(self):
+ stdout = "%s/%d.stdout " % (self.emulation.run_home, self.pid)
+ stderr = "%s/%d.stderr " % (self.emulation.run_home, self.pid)
+ self._trace_filename["stdout"] = stdout
+ self._trace_filename["stderr"] = stderr
+ self._traces["stdout"] = self.emulation.create("open", stdout, "w")
+ self._traces["stderr"] = self.emulation.create("open", stderr, "w")
+
+ @property
+ def state(self):
+ if self._state == ResourceState.STARTED:
+ retcode = self.emulation.invoke(self.uuid, "poll")
+
+ if retcode:
+ if ret == 0:
+ self.set_stopped()
+ else:
+ out = ""
+ msg = " Failed to execute command '%s'" % self.get("command")
+ err = self.trace("stderr")
+ self.error(msg, out, err)
+ self.do_fail()
+
+ return self._state
+
--- /dev/null
+#
+# 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.resource import ResourceManager, clsinit_copy, \
+ ResourceState, reschedule_delay
+from nepi.execution.attribute import Flags
+from nepi.execution.trace import TraceAttr
+
+@clsinit_copy
+class NetNSBase(ResourceManager):
+ _rtype = "abstract::netns::Object"
+ _backend_type = "netns"
+
+ def __init__(self, ec, guid):
+ super(NetNSBase, self).__init__(ec, guid)
+ self._uuid = None
+ self._connected = set()
+ self._trace_filename = dict()
+
+ @property
+ def connected(self):
+ return self._connected
+
+ @property
+ def uuid(self):
+ return self._uuid
+
+ def trace(self, name, attr = TraceAttr.ALL, block = 512, offset = 0):
+ filename = self._trace_filename.get(name)
+ if not filename:
+ self.error("Can not resolve trace %s. Did you enabled it?" % name)
+ return ""
+
+ return self.emulation.trace(filename, attr, block, offset)
+
+ @property
+ def _rms_to_wait(self):
+ """ Returns the collection of RMs that this RM needs to
+ wait for before start
+
+ This method should be overriden to wait for other
+ objects to be deployed before proceeding with the deployment
+
+ """
+ raise RuntimeError, "No dependencies defined!"
+
+ def _instantiate_object(self):
+ pass
+
+ def _wait_rms(self):
+ rms = set()
+ for rm in self._rms_to_wait:
+ if rm is not None:
+ rms.add(rm)
+
+ """ Returns True if dependent RMs are not yer READY, False otherwise"""
+ for rm in rms:
+ if rm.state < ResourceState.READY:
+ return True
+ return False
+
+ def do_provision(self):
+ self._instantiate_object()
+
+ self.info("Provisioning finished")
+
+ super(NetNSBase, self).do_provision()
+
+ def do_deploy(self):
+ if self._wait_rms():
+ self.debug("---- RESCHEDULING DEPLOY ----" )
+ self.ec.schedule(reschedule_delay, self.deploy)
+ else:
+ self.do_discover()
+ self.do_provision()
+
+ self.set_ready()
+
+ def do_start(self):
+ if self.state == ResourceState.READY:
+ # No need to do anything, simulation.Run() will start every object
+ self.info("Starting")
+ self.set_started()
+ else:
+ msg = " Failed "
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+
+ def do_stop(self):
+ if self.state == ResourceState.STARTED:
+ # No need to do anything, simulation.Destroy() will stop every object
+ self.set_stopped()
+
+ @property
+ def state(self):
+ return self._state
+
+ def get(self, name):
+ #flags = Flags.NoWrite | Flags.NoRead | Flags.Design
+ flags = Flags.Design
+ if self.state in [ResourceState.READY, ResourceState.STARTED] \
+ and not self.has_flag(name, flags):
+ return self.emulation.emu_get(self.uuid, name)
+
+ value = super(NetNSBase, self).get(name)
+ if name != "critical":
+ print name, value, "lalal"
+ return value
+
+ def set(self, name, value):
+ #flags = Flags.NoWrite | Flags.NoRead | Flags.Design
+ flags = Flags.Design
+ if self.has_flag(name, flags):
+ out = err = ""
+ msg = " Cannot change Design only attribue %s" % name
+ self.error(msg, out, err)
+ return
+
+ if self.state in [ResourceState.READY, ResourceState.STARTED]:
+ self.emulation.emu_set(self.uuid, name, value)
+
+ value = super(NetNSBase, self).set(name, value)
+ if name != "critical":
+ print name, value, "IEEEEEEEE:"
+
+ return value
+
--- /dev/null
+#
+# 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>
+
+class NetNSClient(object):
+ """ Common Interface for NS3 client classes """
+ def __init__(self):
+ super(NetNSClient, self).__init__()
+
+ def create(self, *args, **kwargs):
+ pass
+
+ def invoke(self, *args, **kwargs):
+ pass
+
+ def set(self, *args, **kwargs):
+ pass
+
+ def get(self, *args, **kwargs):
+ pass
+
+ def flush(self, *args, **kwargs):
+ pass
+
+ def shutdown(self, *args, **kwargs):
+ pass
+
--- /dev/null
+#
+# 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>
+
+class NetNSEmulation(object):
+ @property
+ def client(self):
+ return self._client
+
+ def create(self, *args, **kwargs):
+ return self.client.create(*args, **kwargs)
+
+ def invoke(self, *args, **kwargs):
+ return self.client.invoke(*args, **kwargs)
+
+ def emu_set(self, *args, **kwargs):
+ return self.client.set(*args, **kwargs)
+
+ def emu_get(self, *args, **kwargs):
+ return self.client.get(*args, **kwargs)
+
+ def flush(self, *args, **kwargs):
+ return self.client.flush(*args, **kwargs)
+
+ def shutdown(self, *args, **kwargs):
+ return self.client.shutdown(*args, **kwargs)
+
--- /dev/null
+#
+# 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
+from nepi.resources.netns.netnsbase import NetNSBase
+
+@clsinit_copy
+class NetNSInterface(NetNSBase):
+ _rtype = "asbtract::netns::NodeInterface"
+
+
--- /dev/null
+#
+# 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
+from nepi.resources.netns.netnsbase import NetNSBase
+
+@clsinit_copy
+class NetNSIPv4Address(NetNSBase):
+ _rtype = "netns::IPv4Address"
+
+ @classmethod
+ def _register_attributes(cls):
+ ip = Attribute("ip", "IPv4 address", flags=Flags.Design)
+ prefix = Attribute("prefix", "IPv4 prefix", flags=Flags.Design)
+
+ cls._register_attribute(ip)
+ cls._register_attribute(prefix)
+
+ @property
+ def emulation(self):
+ return self.node.emulation
+
+ @property
+ def node(self):
+ return self.interface.node
+
+ @property
+ def interface(self):
+ from nepi.resources.netns.netnsinterface import NetNSInterface
+ interface = self.get_connected(NetNSInterface.get_rtype())
+
+ if not interface:
+ msg = "IPv4Address not connected to Interface!!"
+ self.error(msg)
+ raise RuntimeError, msg
+
+ return interface[0]
+
+ @property
+ def _rms_to_wait(self):
+ return [self.interface]
+
+ def _instantiate_object(self):
+ self._uuid = self.emulation.invoke(self.interface.uuid, "add_v4_address",
+ self.get("ip"), self.get("prefix"))
+
+
--- /dev/null
+#
+# 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
+from nepi.resources.netns.netnsbase import NetNSBase
+
+@clsinit_copy
+class NetNSNode(NetNSBase):
+ _rtype = "netns::Node"
+
+ @property
+ def emulation(self):
+ from nepi.resources.netns.netnsemulation import NetNSEmulation
+
+ for guid in self.connections:
+ rm = self.ec.get_resource(guid)
+ if isinstance(rm, NetNSEmulation):
+ return rm
+
+ msg = "Node not connected to Emulation"
+ self.error(msg)
+ raise RuntimeError, msg
+
+ @property
+ def _rms_to_wait(self):
+ return [self.emulation]
+
+ def _instantiate_object(self):
+ self._uuid = self.emulation.create("Node")
+
+
--- /dev/null
+#
+# 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
+from nepi.resources.netns.netnsinterface import NetNSInterface
+
+@clsinit_copy
+class NetNSNodeInterface(NetNSInterface):
+ _rtype = "netns::NodeInterface"
+
+ @classmethod
+ def _register_attributes(cls):
+ up = Attribute("up", "Interface up",
+ default = True,
+ type = Types.Bool)
+
+ cls._register_attribute(up)
+
+ @property
+ def emulation(self):
+ return self.node.emulation
+
+ @property
+ def node(self):
+ from nepi.resources.netns.netnsnode import NetNSNode
+ node = self.get_connected(NetNSNode.get_rtype())
+
+ if not node:
+ msg = "Route not connected to Node!!"
+ self.error(msg)
+ raise RuntimeError, msg
+
+ return node[0]
+
+ @property
+ def switch(self):
+ from nepi.resources.netns.netnsswitch import NetNSSwitch
+ switch = self.get_connected(NetNSSwitch.get_rtype())
+ if switch: return switch[0]
+ return None
+
+ @property
+ def _rms_to_wait(self):
+ return [self.node, self.switch]
+
+ def _instantiate_object(self):
+ self._uuid = self.emulation.invoke(self.node.uuid, "add_if")
+ self.emulation.invoke(self.switch.uuid, "connect", self.uuid)
+ self.emulation.emu_set(self.uuid, "up", self.get("up"))
+
+
--- /dev/null
+#
+# 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
+from nepi.resources.netns.netnsbase import NetNSBase
+
+@clsinit_copy
+class NetNSIPv4Route(NetNSBase):
+ _rtype = "netns::IPv4Route"
+
+ @classmethod
+ def _register_attributes(cls):
+ network = Attribute("network", "Network address", flags=Flags.Design)
+ prefix = Attribute("prefix", "IP prefix length", flags=Flags.Design)
+ nexthop = Attribute("nexthop", "Nexthop IP", flags=Flags.Design)
+
+ cls._register_attribute(network)
+ cls._register_attribute(prefix)
+ cls._register_attribute(nexthop)
+
+ @property
+ def emulation(self):
+ return self.node.emulation
+
+ @property
+ def node(self):
+ from nepi.resources.netns.netnsnode import NetNSNode
+ node = self.get_connected(NetNSNode.get_rtype())
+
+ if not node:
+ msg = "Route not connected to Node!!"
+ self.error(msg)
+ raise RuntimeError, msg
+
+ return node[0]
+
+ @property
+ def _rms_to_wait(self):
+ return [self.node]
+
+ def _instantiate_object(self):
+ self._uuid = self.emulation.invoke(self.device.uuid, "add_route",
+ prefix=self.get("network"), prefix_len=self.get("prefix"),
+ nexthop=self.get("nexthop"))
+
+
--- /dev/null
+#
+# 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>
+
+import base64
+import cPickle
+import errno
+import logging
+import os
+import socket
+import sys
+
+from optparse import OptionParser, SUPPRESS_HELP
+
+from netnswrapper import NetNSWrapper
+
+class NetNSWrapperMessage:
+ CREATE = "CREATE"
+ INVOKE = "INVOKE"
+ SET = "SET"
+ GET = "GET"
+ FLUSH = "FLUSH"
+ SHUTDOWN = "SHUTDOWN"
+
+def handle_message(wrapper, msg_type, args, kwargs):
+ if msg_type == NetNSWrapperMessage.SHUTDOWN:
+ wrapper.shutdown()
+
+ return "BYEBYE"
+
+ if msg_type == NetNSWrapperMessage.CREATE:
+ clazzname = args.pop(0)
+
+ return wrapper.create(clazzname, *args)
+
+ if msg_type == NetNSWrapperMessage.INVOKE:
+ uuid = args.pop(0)
+ operation = args.pop(0)
+
+ return wrapper.invoke(uuid, operation, *args, **kwargs)
+
+ if msg_type == NetNSWrapperMessage.GET:
+ uuid = args.pop(0)
+ name = args.pop(0)
+
+ return wrapper.get(uuid, name)
+
+ if msg_type == NetNSWrapperMessage.SET:
+ uuid = args.pop(0)
+ name = args.pop(0)
+ value = args.pop(0)
+
+ return wrapper.set(uuid, name, value)
+
+ if msg_type == NetNSWrapperMessage.FLUSH:
+ # Forces flushing output and error streams.
+ # NS-3 output will stay unflushed until the program exits or
+ # explicit invocation flush is done
+ sys.stdout.flush()
+ sys.stderr.flush()
+
+ wrapper.logger.debug("FLUSHED")
+
+ return "FLUSHED"
+
+def create_socket(socket_name):
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.bind(socket_name)
+ return sock
+
+def recv_msg(conn):
+ msg = []
+ chunk = ''
+
+ while '\n' not in chunk:
+ try:
+ chunk = conn.recv(1024)
+ except (OSError, socket.error), e:
+ if e[0] != errno.EINTR:
+ raise
+ # Ignore eintr errors
+ continue
+
+ if chunk:
+ msg.append(chunk)
+ else:
+ # empty chunk = EOF
+ break
+
+ msg = ''.join(msg).strip()
+
+ # The message is formatted as follows:
+ # MESSAGE_TYPE|args|kwargs
+ #
+ # where MESSAGE_TYPE, args and kwargs are pickld and enoded in base64
+
+ def decode(item):
+ item = base64.b64decode(item).rstrip()
+ return cPickle.loads(item)
+
+ decoded = map(decode, msg.split("|"))
+
+ # decoded message
+ dmsg_type = decoded.pop(0)
+ dargs = list(decoded.pop(0)) # transforming touple into list
+ dkwargs = decoded.pop(0)
+
+ return (dmsg_type, dargs, dkwargs)
+
+def send_reply(conn, reply):
+ encoded = base64.b64encode(cPickle.dumps(reply))
+ conn.send("%s\n" % encoded)
+
+def get_options():
+ usage = ("usage: %prog -S <socket-name> -D <enable-dump> -v ")
+
+ 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")
+
+ parser.add_option("-D", "--enable-dump", dest="enable_dump",
+ help = "Enable dumping the remote executed commands to a script "
+ "in order to later reproduce and debug the experiment",
+ action = "store_true",
+ default = False)
+
+ parser.add_option("-v", "--verbose",
+ help="Print debug output",
+ action="store_true",
+ dest="verbose", default=False)
+
+ (options, args) = parser.parse_args()
+
+ return (options.socket_name, options.verbose, options.enable_dump)
+
+def run_server(socket_name, level = logging.INFO,
+ enable_dump = False):
+
+ ###### wrapper instantiation
+
+ wrapper = NetNSWrapper(loglevel=level, enable_dump = enable_dump)
+
+ wrapper.logger.info("STARTING...")
+
+ # create unix socket to receive instructions
+ sock = create_socket(socket_name)
+ sock.listen(0)
+
+ # wait for messages to arrive and process them
+ stop = False
+
+ while not stop:
+ conn, addr = sock.accept()
+ conn.settimeout(5)
+
+ try:
+ (msg_type, args, kwargs) = recv_msg(conn)
+ except socket.timeout, e:
+ # Ingore time-out
+ continue
+
+ if not msg_type:
+ # Ignore - connection lost
+ break
+
+ if msg_type == NetNSWrapperMessage.SHUTDOWN:
+ stop = True
+
+ try:
+ reply = handle_message(wrapper, msg_type, args, kwargs)
+ except:
+ import traceback
+ err = traceback.format_exc()
+ wrapper.logger.error(err)
+ raise
+
+ try:
+ send_reply(conn, reply)
+ except socket.error:
+ break
+
+ wrapper.logger.info("EXITING...")
+
+if __name__ == '__main__':
+
+ (socket_name, verbose, enable_dump) = get_options()
+
+ ## configure logging
+ FORMAT = "%(asctime)s %(name)s %(levelname)-4s %(message)s"
+ level = logging.DEBUG if verbose else logging.INFO
+
+ logging.basicConfig(format = FORMAT, level = level)
+
+ ## Run the server
+ run_server(socket_name, level, enable_dump)
+
--- /dev/null
+#
+# 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
+from nepi.resources.netns.netnsbase import NetNSBase
+
+@clsinit_copy
+class NetNSSwitch(NetNSBase):
+ _rtype = "netns::Switch"
+
+ @classmethod
+ def _register_attributes(cls):
+ up = Attribute("up", "Switch up",
+ default = True,
+ type = Types.Bool)
+
+ cls._register_attribute(up)
+
+ @property
+ def emulation(self):
+ return self.node.emulation
+
+ @property
+ def node(self):
+ return self.interface.node
+
+ @property
+ def interface(self):
+ from nepi.resources.netns.netnsinterface import NetNSInterface
+ interface = self.get_connected(NetNSInterface.get_rtype())
+
+ if not interface:
+ msg = "Switch not connected to any Interface!!"
+ self.error(msg)
+ raise RuntimeError, msg
+
+ return interface[0]
+
+ @property
+ def _rms_to_wait(self):
+ return [self.emulation]
+
+ def _instantiate_object(self):
+ self._uuid = self.emulation.create("Switch")
+ self.emulation.emu_set(self.uuid, "up", self.get("up"))
+
def create(self, clazzname, *args):
""" This method should be used to construct netns objects """
+ import netns
if clazzname not in ['open'] and not hasattr(netns, clazzname):
msg = "Type %s not supported" % (clazzname)
self.debuger.dump_shutdown()
########
+ ### FLUSH PIPES
sys.stdout.flush()
sys.stderr.flush()
+ ### RELEASE OBJECTS
+ del self._objects
+
### DEBUG
self.logger.debug("SHUTDOWN")
########
self.dump_to_script(command)
- def dump_start(self):
- if not self.enabled:
- return
-
- command = "wrapper.start()\n\n"
- self.dump_to_script(command)
-
- def dump_stop(self, time = None):
- if not self.enabled:
- return
-
- command = ("wrapper.stop(time=%(time)s)\n\n"
- ) % dict({
- "time": self.format_value(time) if time else "None",
- })
-
- self.dump_to_script(command)
-
def dump_shutdown(self):
if not self.enabled:
return
command = "wrapper.shutdown()\n\n"
self.dump_to_script(command)
- def dump_add_static_route(self, uuid, args):
- if not self.enabled:
- return
-
- command = ("args = %(args)s\n"
- "wrapper._add_static_route(%(uuid)s, *args)\n\n"
- ) % dict({
- "uuid": self.format_value(uuid),
- "args": self.format_args(args),
- })
-
- self.dump_to_script(command)
-
def format_value(self, value):
if isinstance(value, str) and value.startswith("uuid"):
return value.replace("-", "")
self.info("Starting")
self.set_started()
else:
- msg = " Failed "
+ msg = "Failed"
self.error(msg, out, err)
raise RuntimeError, msg
def do_stop(self):
if self.state == ResourceState.STARTED:
# No need to do anything, simulation.Destroy() will stop every object
- self.info("Stopping command '%s'" % command)
+ self.info("Stopping")
self.set_stopped()
@property
--- /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>
+
+# Test based on netns test/test_core.py file test_run_ping_routing test
+#
+
+from nepi.resources.netns.netnsserver import run_server
+from nepi.resources.linux.netns.netnsclient import LinuxNetNSClient
+
+from test_utils import skipIf
+
+import os
+import threading
+import time
+import unittest
+
+class DummyEmulation(object):
+ def __init__(self, socket_name):
+ self.socket_name = socket_name
+ self.node = dict({'hostname': 'localhost'})
+
+ @property
+ def remote_socket(self):
+ return self.socket_name
+
+class LinuxNetNSClientTest(unittest.TestCase):
+ def setUp(self):
+ self.socket_name = os.path.join("/", "tmp", "NetNSWrapperServer.sock")
+ if os.path.exists(self.socket_name):
+ os.remove(self.socket_name)
+
+ def tearDown(self):
+ os.remove(self.socket_name)
+
+ @skipIf(os.getuid() != 0, "Test requires root privileges")
+ def test_run_ping_routing(self):
+ thread = threading.Thread(target = run_server,
+ args = [self.socket_name])
+
+ thread.setDaemon(True)
+ thread.start()
+
+ time.sleep(3)
+
+ # Verify that the communication socket was created
+ self.assertTrue(os.path.exists(self.socket_name))
+
+ # Create a dummy simulation object
+ emulation = DummyEmulation(self.socket_name)
+
+ # Instantiate the NS3 client
+ client = LinuxNetNSClient(emulation)
+
+ ### create 3 nodes
+ #n1 = netns.Node()
+ #n2 = netns.Node()
+ #n3 = netns.Node()
+ n1 = client.create("Node")
+ n2 = client.create("Node")
+ n3 = client.create("Node")
+
+ ### add interfaces to nodes
+ #i1 = n1.add_if()
+ #i2a = n2.add_if()
+ #i2b = n2.add_if()
+ #i3 = n3.add_if()
+ i1 = client.invoke(n1, "add_if")
+ i2a = client.invoke(n2, "add_if")
+ i2b = client.invoke(n2, "add_if")
+ i3 = client.invoke(n3, "add_if")
+
+ ### set interfaces up
+ # i1.up = i2a.up = i2b.up = i3.up = True
+ client.set(i1, "up", True)
+ client.set(i2a, "up", True)
+ client.set(i2b, "up", True)
+ client.set(i3, "up", True)
+
+ ### create 2 switches
+ #l1 = netns.Switch()
+ #l2 = netns.Switch()
+ l1 = client.create("Switch")
+ l2 = client.create("Switch")
+
+ ### connect interfaces to switches
+ #l1.connect(i1)
+ #l1.connect(i2a)
+ #l2.connect(i2b)
+ #l2.connect(i3)
+ client.invoke(l1, "connect", i1)
+ client.invoke(l1, "connect", i2a)
+ client.invoke(l2, "connect", i2b)
+ client.invoke(l2, "connect", i3)
+
+ ### set switched up
+ # l1.up = l2.up = True
+ client.set(l1, "up", True)
+ client.set(l2, "up", True)
+
+ ## add ip addresses to interfaces
+ #i1.add_v4_address('10.0.0.1', 24)
+ #i2a.add_v4_address('10.0.0.2', 24)
+ #i2b.add_v4_address('10.0.1.1', 24)
+ #i3.add_v4_address('10.0.1.2', 24)
+ client.invoke(i1, "add_v4_address", "10.0.0.1", 24)
+ client.invoke(i2a, "add_v4_address", "10.0.0.2", 24)
+ client.invoke(i2b, "add_v4_address", "10.0.1.1", 24)
+ client.invoke(i3, "add_v4_address", "10.0.1.2", 24)
+
+ ## add routes to nodes
+ #n1.add_route(prefix = '10.0.1.0', prefix_len = 24,
+ # nexthop = '10.0.0.2')
+ #n3.add_route(prefix = '10.0.0.0', prefix_len = 24,
+ # nexthop = '10.0.1.1')
+ client.invoke(n1, "add_route", prefix="10.0.1.0", prefix_len=24,
+ nexthop="10.0.0.2")
+ client.invoke(n3, "add_route", prefix "10.0.0.0", prefix_len=24,
+ nexthop="10.0.1.1")
+
+ ## launch pings
+ #a1 = n1.Popen(['ping', '-qc1', '10.0.1.2'], stdout = null)
+ #a2 = n3.Popen(['ping', '-qc1', '10.0.0.1'], stdout = null)
+ path1 = "/tmp/netns_file1"
+ path2 = "/tmp/netns_file2"
+ file1 = client.create("open", path1, "w")
+ file2 = client.create("open", path2, "w")
+ a1 = client.invoke(n1, "Popen", ["ping", "-qc1", "10.0.1.2"], stdout=file1)
+ a2 = client.invoke(n3, "Popen", ["ping", "-qc1", "10.0.0.1"], stdout=file2)
+
+ ## get ping status
+ p1 = None
+ p2 = None
+ while p1 is None or p2 is None:
+ p1 = client.invoke(a1, "poll")
+ p2 = client.invoke(a2, "poll")
+
+ stdout1 = open(path1, "r")
+ stdout2 = open(path2, "r")
+
+ s1 = stdout1.read()
+ s2 = stdout2.read()
+
+ print s1, s2
+
+ expected = "1 packets transmitted, 1 received, 0% packet loss"
+ self.assertTrue(s1.find(expected) > -1)
+ self.assertTrue(s2.find(expected) > -1)
+
+ # wait until emulation is over
+ client.shutdown()
+
+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.execution.trace import TraceAttr
+
+from test_utils import skipIfNotAlive
+
+import os
+import time
+import unittest
+
+def add_node(ec, emu, addr, prefix):
+ node = ec.register_resource("netns::Node")
+ ec.register_connection(node, emu)
+
+ iface = ec.register_resource("netns::NodeInterface")
+ ec.register_connection(iface, node)
+
+ ip = ec.register_resource("netns::IPv4Address")
+ ec.set(ip, "ip", addr)
+ ec.set(ip, "prefix", prefix)
+ ec.register_connection(ip, iface)
+
+ print ec.get(ip, "ip"), addr
+ print ec.get(ip, "prefix"), prefix
+
+ return node, iface
+
+class LinuxNetNSEmulationTest(unittest.TestCase):
+ def setUp(self):
+ self.fedora_host = "mimas.inria.fr"
+ self.fedora_user = "aquereil"
+ self.fedora_identity = "%s/.ssh/id_rsa" % (os.environ['HOME'])
+
+ @skipIfNotAlive
+ def t_ping(self, host, user = None, identity = None):
+ ec = ExperimentController(exp_id = "test-netns-p2p-ping")
+
+ node = ec.register_resource("LinuxNode")
+ if host == "localhost":
+ ec.set(node, "hostname", "localhost")
+ else:
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "identity", identity)
+
+ ec.set(node, "cleanProcesses", True)
+ #ec.set(node, "cleanHome", True)
+
+ emu = ec.register_resource("LinuxNetNSEmulation")
+ ec.set(emu, "verbose", True)
+ ec.register_connection(emu, node)
+
+ netnode1, iface1 = add_node(ec, emu, "10.0.0.1", "24")
+ netnode2, iface2 = add_node(ec, emu, "10.0.0.2", "24")
+
+ switch = ec.register_resource("netns::Switch")
+ ec.register_connection(iface1, switch)
+ ec.register_connection(iface2, switch)
+
+ ping = ec.register_resource("netns::Application")
+ ec.set(ping, "command", "10.0.0.2")
+ ec.register_connection(ping, netnode1)
+
+ ec.deploy()
+
+ ec.wait_finished([ping])
+
+ stdout = ec.trace(ping, "stdout")
+
+ expected = "20 packets transmitted, 20 received, 0% packet loss"
+ self.assertTrue(stdout.find(expected) > -1)
+
+ ec.shutdown()
+
+ def ztest_ping_fedora(self):
+ self.t_ping(self.fedora_host, self.fedora_user, self.fedora_identity)
+
+ def test_ping_local(self):
+ self.t_ping("localhost")
+
+
+if __name__ == '__main__':
+ unittest.main()
+
stdout1 = open(path1, "r")
stdout2 = open(path2, "r")
- print stdout1.read(), stdout2.read()
+ s1 = stdout1.read()
+ s2 = stdout2.read()
+
+ expected = "1 packets transmitted, 1 received, 0% packet loss"
+ self.assertTrue(s1.find(expected) > -1)
+ self.assertTrue(s2.find(expected) > -1)
+
+ wrapper.shutdown()
if __name__ == '__main__':
unittest.main()