--- /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 clsinit_copy, ResourceState, \
+ reschedule_delay
+from nepi.resources.linux.application import LinuxApplication
+from nepi.util.sshfuncs import ProcStatus
+from nepi.util.timefuncs import tnow, tdiffsec
+
+import os
+import socket
+import time
+
+@clsinit_copy
+class LinuxGRETunnel(LinuxApplication):
+ _rtype = "LinuxGRETunnel"
+ _help = "Constructs a tunnel between two Linux endpoints using a UDP connection "
+ _backend = "linux"
+
+ @classmethod
+ def _register_attributes(cls):
+ bwlimit = Attribute("bwLimit",
+ "Specifies the interface's emulated bandwidth in bytes "
+ "per second.",
+ type = Types.Integer,
+ flags = Flags.Design)
+
+ cls._register_attribute(bwlimit)
+
+ def __init__(self, ec, guid):
+ super(LinuxGRETunnel, self).__init__(ec, guid)
+
+ def log_message(self, msg):
+ return " guid %d - GRE 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")
+ cipher = self.get("cipher")
+ cipher_key = self.get("cipherKey")
+ bwlimit = self.get("bwLimit")
+ txqueuelen = self.get("txQueueLen")
+ udp_connect_command = endpoint.udp_connect_command(
+ remote_ip, local_port_file, remote_port_file,
+ ret_file, cipher, cipher_key, bwlimit, txqueuelen)
+
+ # 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.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:
+ 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 do_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.set_provisioned()
+
+ def do_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:
+ self.do_discover()
+ self.do_provision()
+
+ self.set_ready()
+
+ def do_start(self):
+ if self.state == ResourceState.READY:
+ command = self.get("command")
+ self.info("Starting command '%s'" % command)
+
+ self.set_started()
+ else:
+ msg = " Failed to execute command '%s'" % command
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+
+ def do_stop(self):
+ """ Stops application execution
+ """
+ if self.state == ResourceState.STARTED:
+ 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 (proc1.poll() and err1) or (proc2.poll() and err2):
+ # check if execution errors occurred
+ msg = " Failed to STOP tunnel"
+ self.error(msg, err1, err2)
+ raise RuntimeError, msg
+
+ self.set_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:
+ if self._pid1 and self._ppid1 and self._pid2 and self._ppid2:
+ # Make sure the process is still running in background
+ # No execution errors occurred. Make sure the background
+ # process with the recorded pid is still running.
+ status1 = self.endpoint1.node.status(self._pid1, self._ppid1)
+ status2 = self.endpoint2.node.status(self._pid2, self._ppid2)
+
+ if status1 == ProcStatus.FINISHED and \
+ status2 == ProcStatus.FINISHED:
+
+ # 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 = "Error occurred in tunnel"
+ self.error(msg, err1, err2)
+ self.fail()
+ else:
+ self.set_stopped()
+
+ 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(20):
+ (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)
+ 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>
+
+from nepi.execution.resource import clsinit_copy, ResourceState, \
+ reschedule_delay
+from nepi.resources.linux.application import LinuxApplication
+from nepi.util.timefuncs import tnow, tdiffsec
+
+import os
+import time
+
+state_check_delay = 0.5
+
+@clsinit_copy
+class LinuxTunnel(LinuxApplication):
+ _rtype = "abstract::LinuxTunnel"
+ _help = "Constructs a tunnel between two Linux endpoints"
+ _backend = "linux"
+
+ def __init__(self, ec, guid):
+ super(LinuxTunnel, self).__init__(ec, guid)
+ self._home = "tunnel-%s" % self.guid
+
+ 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 initiate_connection(self, endpoint, remote_endpoint):
+ raise NotImplementedError
+
+ def establish_connection(self, endpoint, remote_endpoint, data):
+ raise NotImplementedError
+
+ def verify_connection(self, endpoint, remote_endpoint):
+ raise NotImplementedError
+
+ def terminate_connection(self, endpoint, remote_endpoint):
+ raise NotImplementedError
+
+ def check_state_connection(self, endpoint, remote_endpoint):
+ raise NotImplementedError
+
+ def do_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))
+
+ # Start 2 step connection
+ # Initiate connection from endpoint 1 to endpoint 2
+ data1 = self.initiate_connection(self.endpoint1, self.endpoint2)
+
+ # Initiate connection from endpoint 2 to endpoint 1
+ data2 = self.initiate_connection(self.endpoint2, self.endpoint1)
+
+ # Establish connection from endpoint 1 to endpoint 2
+ self.establish_connection(self.endpoint1, self.endpoint2, data2)
+
+ # Establish connection from endpoint 2 to endpoint 1
+ self.establish_connection(self.endpoint2, self.endpoint1, data1)
+
+ # check if connection was successful on both sides
+ self.verify_connection(self.endpoint1, self.endpoint2)
+ self.verify_connection(self.endpoint2, self.endpoint1)
+
+ self.info("Provisioning finished")
+
+ self.set_provisioned()
+
+ def do_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:
+ self.do_discover()
+ self.do_provision()
+
+ self.set_ready()
+
+ def do_start(self):
+ if self.state == ResourceState.READY:
+ command = self.get("command")
+ self.info("Starting command '%s'" % command)
+
+ self.set_started()
+ else:
+ msg = " Failed to execute command '%s'" % command
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+
+ def do_stop(self):
+ """ Stops application execution
+ """
+ if self.state == ResourceState.STARTED:
+ self.info("Stopping tunnel")
+
+ self.terminate_connection(self.endpoint1, self.endpoint2)
+ self.terminate_connection(self.endpoint2, self.endpoint1)
+
+ self.set_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.
+ if tdiffsec(tnow(), self._last_state_check) > state_check_delay:
+
+ self.check_state_connection()
+
+ self._last_state_check = tnow()
+
+ return self._state
+
+
+ def valid_connection(self, guid):
+ # TODO: Validate!
+ return True
+
from nepi.execution.attribute import Attribute, Flags, Types
from nepi.execution.resource import clsinit_copy, ResourceState, \
reschedule_delay
-from nepi.resources.linux.application import LinuxApplication
+from nepi.resources.linux.tunnel import LinuxTunnel
from nepi.util.sshfuncs import ProcStatus
from nepi.util.timefuncs import tnow, tdiffsec
import time
@clsinit_copy
-class UdpTunnel(LinuxApplication):
- _rtype = "UdpTunnel"
+class LinuxUdpTunnel(LinuxTunnel):
+ _rtype = "LinuxUdpTunnel"
_help = "Constructs a tunnel between two Linux endpoints using a UDP connection "
_backend = "linux"
cls._register_attribute(bwlimit)
def __init__(self, ec, guid):
- super(UdpTunnel, self).__init__(ec, guid)
+ super(LinuxUdpTunnel, self).__init__(ec, guid)
self._home = "udp-tunnel-%s" % self.guid
- self._pid1 = None
- self._ppid1 = None
- self._pid2 = None
- self._ppid2 = None
+ self._pids = dict()
def log_message(self, msg):
- return " guid %d - tunnel %s - %s - %s " % (self.guid,
+ return " guid %d - udptunnel %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")
+ def initiate_connection(self, endpoint, remote_endpoint):
cipher = self.get("cipher")
cipher_key = self.get("cipherKey")
bwlimit = self.get("bwLimit")
txqueuelen = self.get("txQueueLen")
+
+ # Return the command to execute to initiate the connection to the
+ # other endpoint
+ connection_run_home = self.run_home(endpoint)
udp_connect_command = endpoint.udp_connect_command(
- remote_ip, local_port_file, remote_port_file,
- ret_file, cipher, cipher_key, bwlimit, txqueuelen)
+ remote_endpoint, connection_run_home,
+ cipher, cipher_key, bwlimit, txqueuelen)
# upload command to connect.sh script
shfile = os.path.join(self.app_home(endpoint), "udp-connect.sh")
# wait until port is written to file
port = self.wait_local_port(endpoint)
- return (port, pid, ppid)
- def do_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))
+ self._pids[endpoint] = (pid, ppid)
- # 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)
+ return port
- # 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)
+ def establish_connection(self, endpoint, remote_endpoint, port):
+ self.upload_remote_port(endpoint, port)
- # 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)
+ def verify_connection(self, endpoint, remote_endpoint):
+ self.wait_result(endpoint)
- # check if connection was successful on both sides
- self.wait_result(self.endpoint1)
- self.wait_result(self.endpoint2)
-
- self.info("Provisioning finished")
-
- self.set_provisioned()
+ def terminate_connection(self, endpoint, remote_endpoint):
+ pid, ppid = self._pids[endpoint]
- def do_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:
- self.do_discover()
- self.do_provision()
-
- self.set_ready()
-
- def do_start(self):
- if self.state == ResourceState.READY:
- command = self.get("command")
- self.info("Starting command '%s'" % command)
-
- self.set_started()
- else:
- msg = " Failed to execute command '%s'" % command
- self.error(msg, out, err)
- raise RuntimeError, msg
+ if pid and ppid:
+ (out, err), proc = endpoint.node.kill(pid, ppid,
+ sudo = True)
- def do_stop(self):
- """ Stops application execution
- """
- if self.state == ResourceState.STARTED:
- 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 (proc1.poll() and err1) or (proc2.poll() and err2):
- # check if execution errors occurred
- msg = " Failed to STOP tunnel"
- self.error(msg, err1, err2)
- raise RuntimeError, msg
-
- self.set_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:
- if self._pid1 and self._ppid1 and self._pid2 and self._ppid2:
- # Make sure the process is still running in background
- # No execution errors occurred. Make sure the background
- # process with the recorded pid is still running.
- status1 = self.endpoint1.node.status(self._pid1, self._ppid1)
- status2 = self.endpoint2.node.status(self._pid2, self._ppid2)
-
- if status1 == ProcStatus.FINISHED and \
- status2 == ProcStatus.FINISHED:
-
- # 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 = "Error occurred in tunnel"
- self.error(msg, err1, err2)
- self.fail()
- else:
- self.set_stopped()
-
- self._last_state_check = tnow()
-
- return self._state
+ # check if execution errors occurred
+ if proc.poll() and err:
+ msg = " Failed to STOP tunnel"
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+
+ def check_state_connection(self):
+ # Make sure the process is still running in background
+ # No execution errors occurred. Make sure the background
+ # process with the recorded pid is still running.
+ pid1, ppid1 = self._pids[self.endpoint1]
+ pid2, ppid2 = self._pids[self.endpoint2]
+
+ status1 = self.endpoint1.node.status(pid1, ppid1)
+ status2 = self.endpoint2.node.status(pid2, ppid2)
+
+ if status1 == ProcStatus.FINISHED and \
+ status2 == ProcStatus.FINISHED:
+
+ # 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 = "Error occurred in tunnel"
+ self.error(msg, err1, err2)
+ self.fail()
+ else:
+ self.set_stopped()
def wait_local_port(self, endpoint):
""" Waits until the local_port file for the endpoint is generated,
text = True,
overwrite = False)
- def valid_connection(self, guid):
- # TODO: Validate!
- return True
import passfd
import socket
import vsys
-from optparse import OptionParser, SUPPRESS_HELP
-
-# TODO: GRE OPTION!! CONFIGURE THE VIF-UP IN GRE MODE!!
+from optparse import OptionParser
STOP_MSG = "STOP"
PASSFD_MSG = "PASSFD"
def get_options():
usage = ("usage: %prog -t <vif-type> -a <ip4-address> -n <net-prefix> "
- "-s <snat> -p <pointopoint> -f <if-name-file> -S <socket-name>")
+ "-s <snat> -p <pointopoint> -q <txqueuelen> -f <vif-name-file> "
+ "-S <socket-name>")
parser = OptionParser(usage = usage)
help = "Peer end point for the interface ", default = None,
type="str")
- parser.add_option("-f", "--if-name-file", dest="if_name_file",
- help = "File to store the interface name assigned by the OS",
+ parser.add_option("-q", "--txqueuelen", dest="txqueuelen",
+ help = "Size of transmision queue. Defaults to 0.",
+ default = 0,
+ type="int")
+
+ parser.add_option("-f", "--vif-name-file", dest="vif_name_file",
+ help = "File to store the virtual interface name assigned by the OS",
default = "if_name", type="str")
parser.add_option("-S", "--socket-name", dest="socket_name",
if options.vif_type and options.vif_type == "IFF_TUN":
vif_type = vsys.IFF_TUN
- return (vif_type, options.ip4_address, options.net_prefix, options.snat,
- options.pointopoint, options.if_name_file, options.socket_name)
+ return (vif_type, options.ip4_address, options.net_prefix,
+ options.snat, options.pointopoint, options.txqueuelen,
+ options.vif_name_file, options.socket_name)
if __name__ == '__main__':
- (vif_type, ip4_address, net_prefix, snat, pointopoint,
- if_name_file, socket_name) = get_options()
+ (vif_type, ip4_address, net_prefix, snat, pointopoint,
+ txqueuelen, vif_name_file, socket_name) = get_options()
- (fd, if_name) = vsys.fd_tuntap(vif_type)
- vsys.vif_up(if_name, ip4_address, net_prefix, snat, pointopoint)
-
+ (fd, vif_name) = vsys.fd_tuntap(vif_type)
+
+ vsys.vif_up(vif_name, ip4_address, net_prefix, snat = snat,
+ pointopoint = pointopoint, txqueuelen = txqueuelen)
+
# Saving interface name to 'if_name_file
- f = open(if_name_file, 'w')
- f.write(if_name)
+ f = open(vif_name_file, 'w')
+ f.write(vif_name)
f.close()
# create unix socket to receive instructions
# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
import base64
-import errno
-import vsys
import socket
-from optparse import OptionParser, SUPPRESS_HELP
+import vsys
+
+from optparse import OptionParser
STOP_MSG = "STOP"
def get_options():
- usage = ("usage: %prog -S <socket-name>")
+ usage = ("usage: %prog -N <vif-name> -D <delete> -S <socket-name>")
parser = OptionParser(usage = usage)
+ parser.add_option("-N", "--vif-name", dest="vif_name",
+ help = "The name of the virtual interface, or a "
+ "unique numeric identifier to name the interface "
+ "if GRE mode is used.",
+ type="str")
+
+ parser.add_option("-D", "--delete", dest="delete",
+ action="store_true",
+ default = False,
+ help="Removes virtual interface if GRE mode was used")
+
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)
+ return (options.vif_name, options.delete, options.socket_name)
if __name__ == '__main__':
- (socket_name) = get_options()
-
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- sock.connect(socket_name)
- encoded = base64.b64encode(STOP_MSG)
- sock.send("%s\n" % encoded)
- reply = sock.recv(1024)
- reply = base64.b64decode(reply)
-
- print reply
+ (vif_name, delete, socket_name) = get_options()
+ # If a socket name is sent, send the STOP message and wait for a reply
+ if socket_name:
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(socket_name)
+ encoded = base64.b64encode(STOP_MSG)
+ sock.send("%s\n" % encoded)
+ reply = sock.recv(1024)
+ reply = base64.b64decode(reply)
+ print reply
+ # Else, use the vsys interface to set the virtual interface down
+ elif vif_name:
+ vsys.vif_down(vif_name, delete = delete)
import tunchannel
import vsys
-from optparse import OptionParser, SUPPRESS_HELP
+from optparse import OptionParser
PASSFD_MSG = "PASSFD"
# xxx: There seems to be a weird behavior where
# even if the file exists and had the port number,
# the read operation returns empty string!
- # Maybe a raise condition?
+ # Maybe a race condition?
for i in xrange(10):
f = open(remote_port_file, 'r')
remote_port = f.read()
--- /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 vsys
+
+from optparse import OptionParser
+
+def get_options():
+ usage = ("usage: %prog -N <vif-name> -t <vif-type> -a <ip4-address> "
+ "-n <net-prefix> -s <snat> -p <pointopoint> "
+ "-q <txqueuelen> -g <gre_key> -G <gre_remote> ")
+
+ parser = OptionParser(usage = usage)
+
+ parser.add_option("-N", "--vif-name", dest="vif_name",
+ help = "The name of the virtual interface, or a "
+ "unique numeric identifier to name the interface "
+ "if GRE mode is used.",
+ type="str")
+
+ 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("-a", "--ip4-address", dest="ip4_address",
+ help = "IPv4 address to assign to interface. It must belong to the "
+ "network segment owned by the slice, given by the vsys_vnet tag. ",
+ type="str")
+
+ parser.add_option("-n", "--net-prefix", dest="net_prefix",
+ help = "IPv4 network prefix for the interface. It must be the one "
+ "given by the slice's vsys_vnet tag. ",
+ type="int")
+
+ parser.add_option("-s", "--snat", dest="snat",
+ action="store_true",
+ default = False,
+ help="Enable SNAT for the interface")
+
+ parser.add_option("-p", "--pointopoint", dest="pointopoint",
+ help = "Peer end point for the interface. ",
+ default = None,
+ type="str")
+
+ parser.add_option("-q", "--txqueuelen", dest="txqueuelen",
+ help = "Size of transmision queue. Defaults to 0.",
+ default = 0,
+ type="int")
+
+ parser.add_option("-g", "--gre-key", dest="gre_key",
+ help = "When set, enables GRE mode with the corresponding GRE key.",
+ default = None,
+ type="str")
+
+ parser.add_option("-G", "--gre-remote", dest="gre_remote",
+ help = "Remote endpoint (public IP) for the GRE tunnel.",
+ default = None,
+ 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 (options.vif_name, vif_type, options.ip4_address,
+ options.net_prefix, options.snat, options.pointopoint,
+ options.txqueuelen, options.gre_key, options.gre_remote)
+
+if __name__ == '__main__':
+
+ (vif_name, vif_type, ip4_address, net_prefix, snat, pointopoint,
+ pointopoint, txqueuelen, gre_key, gre_remote) = get_options()
+
+ if (gre_key):
+ import pwd
+ import getpass
+
+ slicename = getpass.getuser()
+ sliceid = pwd.getpwnam(slicename).pw_uid
+
+ if vif_type == vsys.IFF_TAP:
+ vif_prefix = "tap"
+ else:
+ vif_prefix = "tun"
+
+ # if_name should be a unique numeric vif id
+ vif_name = "%s%s-%d" % (vif_prefix, sliceid, vif_name)
+
+ vsys.vif_up(vif_name, ip4_address, net_prefix, snat = snat,
+ pointopoint = pointopoint, txqueuelen = txqueuelen,
+ gre_key = gre_key, gre_remote = gre_remote)
+
from nepi.util.timefuncs import tnow, tdiffsec
import os
+import socket
import time
# TODO:
return None
def upload_sources(self):
- # upload vif-creation python script
+ scripts = []
+
+ # vif-creation python script
pl_vif_create = os.path.join(os.path.dirname(__file__), "scripts",
"pl-vif-create.py")
- self.node.upload(pl_vif_create,
- os.path.join(self.node.src_dir, "pl-vif-create.py"),
- overwrite = False)
-
- # upload vif-stop python script
- pl_vif_stop = os.path.join(os.path.dirname(__file__), "scripts",
- "pl-vif-stop.py")
+ scripts.append(pl_vif_create)
+
+ # vif-up python script
+ pl_vif_up = os.path.join(os.path.dirname(__file__), "scripts",
+ "pl-vif-up.py")
+
+ scripts.append(pl_vif_up)
- self.node.upload(pl_vif_stop,
- os.path.join(self.node.src_dir, "pl-vif-stop.py"),
- overwrite = False)
+ # vif-down python script
+ pl_vif_down = os.path.join(os.path.dirname(__file__), "scripts",
+ "pl-vif-down.py")
+
+ scripts.append(pl_vif_down)
- # upload vif-connect python script
+ # udp-connect python script
pl_vif_connect = os.path.join(os.path.dirname(__file__), "scripts",
"pl-vif-udp-connect.py")
+
+ scripts.append(pl_vif_connect)
- self.node.upload(pl_vif_connect,
- os.path.join(self.node.src_dir, "pl-vif-udp-connect.py"),
- overwrite = False)
-
- # upload tun-connect python script
+ # tunnel creation python script
tunchannel = os.path.join(os.path.dirname(__file__), "..", "linux",
"scripts", "tunchannel.py")
- self.node.upload(tunchannel,
- os.path.join(self.node.src_dir, "tunchannel.py"),
+ scripts.append(tunchannel)
+
+ # Upload scripts
+ scripts = ";".join(scripts)
+
+ self.node.upload(scripts,
+ os.path.join(self.node.src_dir),
overwrite = False)
# upload stop.sh script
stop_command = self.replace_paths(self._stop_command)
+
self.node.upload(stop_command,
os.path.join(self.app_home, "stop.sh"),
text = True,
# After creating the TAP, the pl-vif-create.py script
# will write the name of the TAP to a file. We wait until
# we can read the interface name from the file.
- if_name = self.wait_if_name()
- self.set("deviceName", if_name)
+ vif_name = self.wait_vif_name()
+ self.set("deviceName", vif_name)
def do_deploy(self):
if not self.node or self.node.state < ResourceState.PROVISIONED:
(out, err), proc = self.execute_command(command,
blocking = True)
+ if err:
+ msg = " Failed to stop command '%s' " % command
+ self.error(msg, out, err)
+
self.set_stopped()
@property
def do_release(self):
# Node needs to wait until all associated RMs are released
# to be released
- from nepi.resources.linux.udptunnel import UdpTunnel
- rms = self.get_connected(UdpTunnel.get_rtype())
+ from nepi.resources.linux.tunnel import LinuxTunnel
+ rms = self.get_connected(LinuxTunnel.get_rtype())
+
for rm in rms:
if rm.state < ResourceState.STOPPED:
self.ec.schedule(reschedule_delay, self.release)
super(PlanetlabTap, self).do_release()
- def wait_if_name(self):
- """ Waits until the if_name file for the command is generated,
- and returns the if_name for the device """
- if_name = None
+ def wait_vif_name(self):
+ """ Waits until the vif_name file for the command is generated,
+ and returns the vif_name for the device """
+ vif_name = None
delay = 0.5
for i in xrange(20):
- (out, err), proc = self.node.check_output(self.run_home, "if_name")
+ (out, err), proc = self.node.check_output(self.run_home, "vif_name")
if proc.poll() > 0:
(out, err), proc = self.node.check_errors(self.run_home)
raise RuntimeError, err
if out:
- if_name = out.strip()
+ vif_name = out.strip()
break
else:
time.sleep(delay)
delay = delay * 1.5
else:
- msg = "Couldn't retrieve if_name"
+ msg = "Couldn't retrieve vif_name"
self.error(msg, out, err)
raise RuntimeError, msg
- return if_name
+ return vif_name
+
+ def udp_connect_command(self, remote_endpoint, connection_run_home,
+ cipher, cipher_key, bwlimit, txqueuelen):
+
+ # Generate UDP connect command
+ remote_ip = socket.gethostbyname(
+ remote_endpoint.node.get("hostname"))
+
+ local_port_file = os.path.join(connection_run_home,
+ "local_port")
+
+ remote_port_file = os.path.join(connection_run_home,
+ "remote_port")
+
+ ret_file = os.path.join(connection_run_home,
+ "ret_file")
- def udp_connect_command(self, remote_ip, local_port_file,
- remote_port_file, ret_file, cipher, cipher_key,
- bwlimit, txqueuelen):
command = ["sudo -S "]
command.append("PYTHONPATH=$PYTHONPATH:${SRC}")
command.append("python ${SRC}/pl-vif-udp-connect.py")
command = " ".join(command)
command = self.replace_paths(command)
+
+ # TODO: RECONFIGUTE THE TAP WITH THE INFORMATION ENDPOINT!
return command
@property
command.append("-t %s" % self.vif_type)
command.append("-a %s" % self.get("ip4"))
command.append("-n %d" % self.get("prefix4"))
- command.append("-f %s " % self.if_name_file)
+ command.append("-f %s " % self.vif_name_file)
command.append("-S %s " % self.sock_name)
+
if self.get("snat") == True:
command.append("-s")
+
if self.get("pointopoint"):
command.append("-p %s" % self.get("pointopoint"))
@property
def _stop_command(self):
- command = ["sudo -S python ${SRC}/pl-vif-stop.py"]
+ command = ["sudo -S python ${SRC}/pl-vif-down.py"]
command.append("-S %s " % self.sock_name)
return " ".join(command)
return "IFF_TAP"
@property
- def if_name_file(self):
- return os.path.join(self.run_home, "if_name")
+ def vif_name_file(self):
+ return os.path.join(self.run_home, "vif_name")
@property
def sock_name(self):
--- /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 skipIfAnyNotAliveWithIdentity
+
+import os
+import time
+import unittest
+
+class GRETunnelTestCase(unittest.TestCase):
+ def setUp(self):
+ #self.host1 = "nepi2.pl.sophia.inria.fr"
+ #self.host2 = "nepi5.pl.sophia.inria.fr"
+ self.host1 = "planetlab1.informatik.uni-erlangen.de"
+ self.host2 = "planetlab1.informatik.uni-goettingen.de"
+ self.user = "inria_nepi"
+ self.identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'])
+ #self.netblock = "192.168.1"
+ self.netblock = "192.168.3"
+
+ @skipIfAnyNotAliveWithIdentity
+ def t_tap_gre_tunnel(self, user1, host1, identity1, user2, host2,
+ identity2):
+
+ ec = ExperimentController(exp_id = "test-tap-gre-tunnel")
+
+ node1 = ec.register_resource("PlanetlabNode")
+ ec.set(node1, "hostname", host1)
+ ec.set(node1, "username", user1)
+ ec.set(node1, "identity", identity1)
+ ec.set(node1, "cleanHome", True)
+ ec.set(node1, "cleanProcesses", True)
+
+ tap1 = ec.register_resource("PlanetlabTap")
+ ec.set(tap1, "ip4", "%s.1" % self.netblock)
+ ec.set(tap1, "prefix4", 24)
+ ec.register_connection(tap1, node1)
+
+ node2 = ec.register_resource("PlanetlabNode")
+ ec.set(node2, "hostname", host2)
+ ec.set(node2, "username", user2)
+ ec.set(node2, "identity", identity2)
+ ec.set(node2, "cleanHome", True)
+ ec.set(node2, "cleanProcesses", True)
+
+ tap2 = ec.register_resource("PlanetlabTap")
+ ec.set(tap2, "ip4", "%s.2" % self.netblock)
+ ec.set(tap2, "prefix4", 24)
+ ec.register_connection(tap2, node2)
+
+ gretun = ec.register_resource("GRETunnel")
+ ec.register_connection(tap1, gretun)
+ ec.register_connection(tap2, gretun)
+
+ app = ec.register_resource("LinuxApplication")
+ cmd = "ping -c3 %s.2" % self.netblock
+ 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()
+
+ @skipIfAnyNotAliveWithIdentity
+ def t_tun_udp_tunnel(self, user1, host1, identity1, user2, host2,
+ identity2):
+
+ ec = ExperimentController(exp_id = "test-tun-gre-tunnel")
+
+ node1 = ec.register_resource("PlanetlabNode")
+ ec.set(node1, "hostname", host1)
+ ec.set(node1, "username", user1)
+ ec.set(node1, "identity", identity1)
+ ec.set(node1, "cleanHome", True)
+ ec.set(node1, "cleanProcesses", True)
+
+ tun1 = ec.register_resource("PlanetlabTun")
+ ec.set(tun1, "ip4", "%s.1" % self.netblock)
+ ec.set(tun1, "pointopoint", "%s.2" % self.netblock)
+ ec.set(tun1, "prefix4", 24)
+ ec.register_connection(tun1, node1)
+
+ node2 = ec.register_resource("PlanetlabNode")
+ ec.set(node2, "hostname", host2)
+ ec.set(node2, "username", user2)
+ ec.set(node2, "identity", identity2)
+ ec.set(node2, "cleanHome", True)
+ ec.set(node2, "cleanProcesses", True)
+
+ tun2 = ec.register_resource("PlanetlabTun")
+ ec.set(tun2, "ip4", "%s.2" % self.netblock)
+ ec.set(tun2, "pointopoint", "%s.1" % self.netblock )
+ ec.set(tun2, "prefix4", 24)
+ ec.register_connection(tun2, node2)
+
+ udptun = ec.register_resource("UdpTunnel")
+ ec.register_connection(tun1, udptun)
+ ec.register_connection(tun2, udptun)
+
+ app = ec.register_resource("LinuxApplication")
+ cmd = "ping -c3 %s.2" % self.netblock
+ 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(tun1, "deviceName")
+ self.assertTrue(if_name.startswith("tun"))
+
+ if_name = ec.get(tun2, "deviceName")
+ self.assertTrue(if_name.startswith("tun"))
+
+ ec.shutdown()
+
+ def test_tap_udp_tunnel(self):
+ self.t_tap_udp_tunnel(self.user, self.host1, self.identity,
+ self.user, self.host2, self.identity)
+
+ def test_tun_udp_tunnel(self):
+ self.t_tun_udp_tunnel(self.user, self.host1, self.identity,
+ self.user, self.host2, self.identity)
+
+if __name__ == '__main__':
+ unittest.main()
+
ec.set(tap2, "prefix4", 24)
ec.register_connection(tap2, node2)
- udptun = ec.register_resource("UdpTunnel")
+ udptun = ec.register_resource("LinuxUdpTunnel")
ec.register_connection(tap1, udptun)
ec.register_connection(tap2, udptun)
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"))
+ vif_name = ec.get(tap1, "deviceName")
+ self.assertTrue(vif_name.startswith("tap"))
- if_name = ec.get(tap2, "deviceName")
- self.assertTrue(if_name.startswith("tap"))
+ vif_name = ec.get(tap2, "deviceName")
+ self.assertTrue(vif_name.startswith("tap"))
ec.shutdown()
@skipIfAnyNotAliveWithIdentity
def t_tun_udp_tunnel(self, user1, host1, identity1, user2, host2, identity2):
- ec = ExperimentController(exp_id = "test-tap-udp-tunnel")
+ ec = ExperimentController(exp_id = "test-tun-udp-tunnel")
node1 = ec.register_resource("PlanetlabNode")
ec.set(node1, "hostname", host1)
ec.set(tun2, "prefix4", 24)
ec.register_connection(tun2, node2)
- udptun = ec.register_resource("UdpTunnel")
+ udptun = ec.register_resource("LinuxUdpTunnel")
ec.register_connection(tun1, udptun)
ec.register_connection(tun2, udptun)
expected = """3 packets transmitted, 3 received, 0% packet loss"""
self.assertTrue(ping.find(expected) > -1)
- if_name = ec.get(tun1, "deviceName")
- self.assertTrue(if_name.startswith("tun"))
+ vif_name = ec.get(tun1, "deviceName")
+ self.assertTrue(vif_name.startswith("tun"))
- if_name = ec.get(tun2, "deviceName")
- self.assertTrue(if_name.startswith("tun"))
+ vif_name = ec.get(tun2, "deviceName")
+ self.assertTrue(vif_name.startswith("tun"))
ec.shutdown()