"nepi.resources.ns3",
"nepi.resources.omf",
"nepi.resources.planetlab",
+ "nepi.resources.planetlab.openvswitch",
"nepi.util"],
package_dir = {"": "src"},
package_data = {
--- /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>
+# Alexandros Kouvakas <alexandros.kouvakas@inria.fr>
+
+
+from nepi.execution.resource import ResourceManager, clsinit_copy, ResourceState
+from nepi.execution.attribute import Attribute, Flags
+from nepi.resources.planetlab.node import PlanetlabNode
+from nepi.resources.linux.application import LinuxApplication
+import os
+
+reschedule_delay = "0.5s"
+
+@clsinit_copy
+class OVSWitch(LinuxApplication):
+
+ _rtype = "OVSWitch"
+ _authorized_connections = ["PlanetlabNode", "OVSPort", "LinuxNode"]
+
+ @classmethod
+ def _register_attributes(cls):
+ """ Register the attributes of OVSWitch RM
+
+ """
+ bridge_name = Attribute("bridge_name", "Name of the switch/bridge",
+ flags = Flags.ExecReadOnly)
+ virtual_ip_pref = Attribute("virtual_ip_pref", "Virtual IP/PREFIX of the switch",
+ flags = Flags.ExecReadOnly)
+ controller_ip = Attribute("controller_ip", "IP of the controller",
+ flags = Flags.ExecReadOnly)
+ controller_port = Attribute("controller_port", "Port of the controller",
+ flags = Flags.ExecReadOnly)
+
+ cls._register_attribute(bridge_name)
+ cls._register_attribute(virtual_ip_pref)
+ cls._register_attribute(controller_ip)
+ cls._register_attribute(controller_port)
+
+ def __init__(self, ec, guid):
+ """
+ :param ec: The Experiment controller
+ :type ec: ExperimentController
+ :param guid: guid of the RM
+ :type guid: int
+ :param creds: Credentials to communicate with the rm
+ :type creds: dict
+
+ """
+ super(OVSWitch, self).__init__(ec, guid)
+ self._pid = None
+ self._ppid = None
+ self._home = "ovswitch-%s" % self.guid
+ self._checks = "ovsChecks-%s" % self.guid
+
+ @property
+ def node(self):
+ node = self.get_connected(PlanetlabNode.rtype())
+ if node: return node[0]
+ return None
+
+ @property
+ def ovs_home(self):
+ return os.path.join(self.node.exp_home, self._home)
+
+ @property
+ def ovs_checks(self):
+ return os.path.join(self.ovs_home, self._checks)
+
+ @property
+ def pid(self):
+ return self._pid
+
+ @property
+ def ppid(self):
+ return self._ppid
+
+# def valid_connection(self, guid):
+# """ Check if the connection with the guid in parameter is possible. Only meaningful connections are allowed.
+
+# :param guid: Guid of the current RM
+# :type guid: int
+# :rtype: Boolean
+
+# """
+# rm = self.ec.get_resource(guid)
+# if rm.rtype() in self._authorized_connections:
+# msg = "Connection between %s %s and %s %s accepted" % \
+# (self.rtype(), self._guid, rm.rtype(), guid)
+# self.debug(msg)
+# return True
+# msg = "Connection between %s %s and %s %s refused" % \
+# (self.rtype(), self._guid, rm.rtype(), guid)
+# self.debug(msg)
+# return False
+
+ def valid_connection(self, guid):
+ # TODO: Validate!
+ return True
+
+ def provision(self):
+ # create home dir for ovs
+ self.node.mkdir(self.ovs_home)
+ # create dir for ovs checks
+ self.node.mkdir(self.ovs_checks)
+
+ def check_sliver_ovs(self):
+ """ Check if sliver-ovs exists. If it does not exist, we interrupt
+ the execution immediately.
+ """
+ cmd = "compgen -c | grep sliver-ovs"
+ out = err = ""
+
+ (out,err), proc = self.node.run_and_wait(cmd, self.ovs_checks,
+ shfile = "check_cmd.sh",
+ pidfile = "check_cmd_pidfile",
+ ecodefile = "check_cmd_exitcode",
+ sudo = True,
+ stdout = "check_cmd_stdout",
+ stderr = "check_cmd_stderr")
+
+ (out, err), proc = self.node.check_output(self.ovs_checks, 'check_cmd_exitcode')
+ if out != "0\n":
+ msg = "Command sliver-ovs does not exist on the VM"
+ self.debug(msg)
+ raise RuntimeError, msg
+ msg = "Command sliver-ovs exists"
+ self.debug(msg)
+
+ def deploy(self):
+ """ Wait until node is associated and deployed
+ """
+ node = self.node
+ if not node or node.state < ResourceState.READY:
+ self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.node.state )
+ self.ec.schedule(reschedule_delay, self.deploy)
+
+ else:
+ try:
+ self.discover()
+ self.provision()
+ self.check_sliver_ovs()
+ self.servers_on()
+ self.create_bridge()
+ self.assign_contr()
+ self.ovs_status()
+ except:
+ self._state = ResourceState.FAILED
+ raise
+
+ self._state = ResourceState.READY
+
+ def servers_on(self):
+ """ Start the openvswitch servers and also checking
+ if they started successfully
+ """
+ self.info("Starting the OVSWitch servers")
+ command = ("sliver-ovs start")
+
+ out = err = ""
+ (out, err), proc = self.node.run_and_wait(command, self.ovs_checks,
+ shfile = "start_srv.sh",
+ pidfile = "start_srv_pidfile",
+ ecodefile = "start_srv_exitcode",
+ sudo = True,
+ raise_on_error = True,
+ stdout = "start_srv_stdout",
+ stderr = "start_srv_stderr")
+
+ (out, err), proc = self.node.check_output(self.ovs_checks, 'start_srv_exitcode')
+
+ if out != "0\n":
+ self.debug("Servers have not started")
+ raise RuntimeError, msg
+
+ cmd = "ps -A | grep ovsdb-server"
+ out = err = ""
+ (out, err), proc = self.node.run_and_wait(cmd, self.ovs_checks,
+ shfile = "status_srv.sh",
+ pidfile = "status_srv_pidfile",
+ ecodefile = "status_srv_exitcode",
+ sudo = True,
+ stdout = "status_srv_stdout",
+ stderr = "status_srv_stderr")
+
+ # Check if the servers are running or not
+ (out, err), proc = self.node.check_output(self.ovs_checks, 'status_srv_exitcode')
+ if out != "0\n":
+ self.debug("Servers are not running")
+ raise RuntimeError, msg
+ self.info("Servers started")
+
+ def del_old_br(self):
+ # TODO: Delete old bridges that might exist maybe by adding atribute
+ """ With ovs-vsctl list-br
+ """
+ pass
+
+ def create_bridge(self):
+ """ Create the bridge/switch and we check if we have any
+ error during the SSH connection
+ """
+ # TODO: Add check for virtual_ip belonging to vsys_tag
+ self.del_old_br()
+
+ if self.get("bridge_name") and self.get("virtual_ip_pref"):
+ bridge_name = self.get("bridge_name")
+ virtual_ip_pref = self.get("virtual_ip_pref")
+ self.info(" Creating the bridge %s and assigning %s" %\
+ (bridge_name, virtual_ip_pref) )
+ cmd = "sliver-ovs create-bridge '%s' '%s'" %\
+ (bridge_name, virtual_ip_pref)
+ out = err = ""
+ (out, err), proc = self.node.run_and_wait(cmd, self.ovs_checks,
+ shfile = "create_br.sh",
+ pidfile = "create_br_pidfile",
+ ecodefile = "create_br_exitcode",
+ sudo = True,
+ stdout = "create_br_stdout",
+ stderr = "create_br_stderr")
+ (out, err), proc = self.node.check_output(self.ovs_checks, 'create_br_exitcode')
+ if out != "0\n":
+ msg = "No such pltap netdev\novs-appctl: ovs-vswitchd: server returned an error"
+ self.debug("Check again the virtual IP")
+ raise RuntimeError, msg
+ self.info("Bridge %s created" % bridge_name)
+
+ else:
+ msg = "No assignment in one or both attributes"
+ self.error(msg)
+ self.debug("Bridge name is %s and virtual_ip_pref is %s" %\
+ (self.get("bridge_name"), self.get("virtual_ip_pref")) )
+ raise AttributeError, msg
+
+ def assign_contr(self):
+ """ Set the controller IP
+ """
+ if self.get("controller_ip") and self.get("controller_port"):
+ controller_ip = self.get("controller_ip")
+ controller_port = self.get("controller_port")
+ self.info("Assigning the controller to the %s" % self.get("bridge_name"))
+ cmd = "ovs-vsctl set-controller %s tcp:%s:%s" %\
+ (self.get("bridge_name"), controller_ip, controller_port)
+ out = err = ""
+ (out, err), proc = self.node.run(cmd, self.ovs_checks,
+ sudo = True,
+ stdout = "stdout",
+ stderr = "stderr")
+ if err != "":
+ self.debug("SSH connection refusing in assign_contr")
+ raise RuntimeError, msg
+ self.info("Controller assigned")
+
+ def ovs_status(self):
+ """ Print the status of the created bridge
+ """
+ cmd = "sliver-ovs show | tail -n +2"
+ out = err = ""
+ (out, err), proc = self.node.run_and_wait(cmd, self.ovs_home,
+ sudo = True,
+ stdout = "show_stdout",
+ stderr = "show_stderr")
+ (out, err), proc = self.node.check_output(self.ovs_home, 'show_stdout')
+ self.info(out)
+
+ def start(self):
+ """ Start the RM. It means nothing special for
+ ovswitch for now.
+ """
+ pass
+
+ def stop(self):
+ """ Stop the RM.It means nothing
+ for ovswitch for now.
+ """
+ pass
+
+ def release(self):
+ """ Delete the bridge and
+ close the servers
+ """
+ # Node needs to wait until all associated RMs are released
+ # to be released
+ from nepi.resources.planetlab.openvswitch.ovsport import OVSPort
+ rm = self.get_connected(OVSPort.rtype())
+
+ if rm[0].state < ResourceState.FINISHED:
+ self.ec.schedule(reschedule_delay, self.release)
+ return
+
+ msg = "Deleting the bridge %s" % self.get('bridge_name')
+ self.info(msg)
+ cmd = "sliver-ovs del-bridge %s" % self.get('bridge_name')
+ (out, err), proc = self.node.run(cmd, self.ovs_checks,
+ sudo = True)
+ cmd = "sliver-ovs stop"
+ (out, err), proc = self.node.run(cmd, self.ovs_checks,
+ sudo = True)
+
+ if proc.poll():
+ self.fail()
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+
+ self._state = ResourceState.RELEASED
+
--- /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>
+# Alexandros Kouvakas <alexandros.kouvakas@gmail.com>
+
+from nepi.execution.attribute import Attribute, Flags, Types
+from nepi.execution.resource import ResourceManager, clsinit_copy, ResourceState
+from nepi.resources.planetlab.openvswitch.ovs import OVSWitch
+from nepi.resources.planetlab.node import PlanetlabNode
+from nepi.resources.linux.application import LinuxApplication
+
+reschedule_delay = "0.5s"
+
+@clsinit_copy
+class OVSPort(LinuxApplication):
+ """
+ .. class:: Class Args :
+
+ :param ec: The Experiment controller
+ :type ec: ExperimentController
+ :param guid: guid of the RM
+ :type guid: int
+ :param creds: Credentials to communicate with the rm
+ :type creds: dict
+
+ """
+
+ _rtype = "OVSPort"
+ _authorized_connections = ["OVSWitch", "Tunnel"]
+
+ @classmethod
+ def _register_attributes(cls):
+ """ Register the attributes of OVSPort RM
+
+ """
+ port_name = Attribute("port_name", "Name of the port",
+ flags = Flags.ExecReadOnly)
+
+ cls._register_attribute(port_name)
+
+ def __init__(self, ec, guid):
+ """
+ :param ec: The Experiment controller
+ :type ec: ExperimentController
+ :param guid: guid of the RM
+ :type guid: int
+
+ """
+ super(OVSPort, self).__init__(ec, guid)
+ self._port_number = None
+ self.port_info = []
+
+ @property
+ def node(self):
+ rm_list = self.get_connected(OVSWitch.rtype())
+ if rm_list:
+ for elt in rm_list:
+ node = elt.get_connected(PlanetlabNode.rtype())
+ if node: return node[0]
+ return node[0]
+
+ @property
+ def ovswitch(self):
+ ovswitch = self.get_connected(OVSWitch.rtype())
+ if ovswitch: return ovswitch[0]
+ return None
+
+ @property
+ def port_number(self):
+ return self._port_number
+
+ def valid_connection(self, guid):
+ # TODO: Validate!
+ return True
+
+# def valid_connection(self, guid):
+# """ Check if the connection is available.
+
+# :param guid: Guid of the current RM
+# :type guid: int
+# :rtype: Boolean
+
+# """
+# rm = self.ec.get_resource(guid)
+# if rm.rtype() in self._authorized_connections:
+# msg = "Connection between %s %s and %s %s accepted" % (self.rtype(), self._guid, rm.rtype(), guid)
+# self.debug(msg)
+# return True
+# msg = "Connection between %s %s and %s %s refused" % (self.rtype(), self._guid, rm.rtype(), guid)
+# self.debug(msg)
+
+ def get_host_ip(self):
+ """ Get the hostname of the node that
+ the port belongs to. We use it for tunnel.
+ """
+ get_host_ip = self.node
+ if not get_host_ip:
+ msg = "info_list is empty"
+ self.debug(msg)
+ raise RuntimeError, msg
+ import socket
+ self.port_info.append(get_host_ip.get('hostname'))
+ self.port_info.append(socket.gethostbyname(self.port_info[0]))
+
+ def create_port(self):
+ """ Create the desired port
+ """
+ port_name = self.get('port_name')
+ if not (port_name or self.ovswitch):
+ msg = "The rm_list is empty or the port name is not assigned\n Failed to create port"
+ self.error(msg)
+ self.debug("ovswitch_list = %s and port_name = %s" % (self.ovswitch, port_name) )
+ raise AttributeError, msg
+
+ self.info("Create the port %s on switch %s" % (port_name, self.ovswitch.get('bridge_name')))
+ self.port_info.append(port_name)
+ self.port_info.append(self.ovswitch.get('virtual_ip_pref'))
+ cmd = "sliver-ovs create-port %s %s" % (self.ovswitch.get('bridge_name'), port_name)
+ self.node.run(cmd, self.ovswitch.ovs_checks,
+ stderr = "stdout-%s" % port_name,
+ stdout = "stderr-%s" % port_name,
+ sudo = True)
+
+ def get_local_end(self):
+ """ Get the local_endpoint of the port
+ """
+ msg = "Discovering the number of the port %s"\
+ % self.get('port_name')
+ self.info(msg)
+
+ command = "sliver-ovs get-local-endpoint %s"\
+ % self.get('port_name')
+ out = err = ""
+ (out, err), proc = self.node.run_and_wait(command, self.ovswitch.ovs_checks,
+ shfile = "port_number-%s.sh" % self.get('port_name'),
+ pidfile = "port_number_pidfile-%s" % self.get('port_name'),
+ ecodefile = "port_number_exitcode-%s" % self.get('port_name'),
+ sudo = True,
+ stdout = "stdout-%s" % self.get('port_name'),
+ stderr = "stderr-%s" % self.get('port_name'))
+
+ if err != "":
+ msg = "No assignment in attribute port_name"
+ self.error(msg)
+ self.debug("You are in the method get_local_end and the port_name = %s" % self.get('port_name'))
+ raise AttributeError, msg
+ self._port_number = None
+ self._port_number = int(out)
+ self.port_info.append(self._port_number)
+ self.info("The number of the %s is %s" % (self.get('port_name'), self._port_number))
+
+ def switch_connect_command(self, local_port_name,
+ remote_ip, remote_port_num):
+ """ Script for switch links
+ """
+ command = ["sliver-ovs"]
+ command.append("set-remote-endpoint ")
+ command.append("%s " % local_port_name)
+ command.append("%s " % remote_ip)
+ command.append("%s " % remote_port_num)
+ command = " ".join(command)
+ command = self.replace_paths(command)
+ return command
+
+ def provision(self):
+ """ Provision the ports.No meaning.
+ """
+ pass
+
+ def discover(self):
+ """ Discover the ports.No meaning
+ """
+ pass
+
+ def deploy(self):
+ """ Wait until ovswitch is started
+ """
+ ovswitch = self.ovswitch
+ if not ovswitch or ovswitch.state < ResourceState.READY:
+ self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.ovswitch.state )
+ self.ec.schedule(reschedule_delay, self.deploy)
+
+ else:
+ try:
+ self.discover()
+ self.provision()
+ self.get_host_ip()
+ self.create_port()
+ self.get_local_end()
+ self.ovswitch.ovs_status()
+ self._state = ResourceState.READY
+ except:
+ self._state = ResourceState.FAILED
+ raise
+
+ def start(self):
+ """ Start the RM. It means nothing special for
+ ovsport for now.
+ """
+ pass
+
+ def stop(self):
+ """ Stop the RM. It means nothing special for
+ ovsport for now.
+ """
+ pass
+
+ def release(self):
+ """ Release the port RM means delete the ports
+ """
+ # OVS needs to wait until all associated RMs are released
+ # to be released
+ from nepi.resources.planetlab.openvswitch.tunnel import Tunnel
+ rm = self.get_connected(Tunnel.rtype())
+ if rm[0].state < ResourceState.FINISHED:
+ self.ec.schedule(reschedule_delay, self.release)
+ return
+
+ msg = "Deleting the port %s" % self.get('port_name')
+ self.info(msg)
+ cmd = "sliver-ovs del_port %s" % self.get('port_name')
+ (out, err), proc = self.node.run(cmd, self.ovswitch.ovs_checks,
+ sudo = True)
+
+ if proc.poll():
+ self.fail()
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+
+ self._state = ResourceState.RELEASED
--- /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>
+# Alexandros Kouvakas <alexandros.kouvakas@gmail.com>
+
+from nepi.execution.attribute import Attribute, Flags, Types
+from nepi.execution.resource import ResourceManager, clsinit_copy, ResourceState
+from nepi.resources.linux.application import LinuxApplication
+from nepi.resources.planetlab.node import PlanetlabNode
+from nepi.resources.planetlab.openvswitch.ovs import OVSWitch
+from nepi.util.timefuncs import tnow, tdiffsec
+
+import os
+import time
+import socket
+
+
+reschedule_delay = "0.5s"
+
+@clsinit_copy
+class Tunnel(LinuxApplication):
+ """
+ .. class:: Class Args :
+
+ :param ec: The Experiment controller
+ :type ec: ExperimentController
+ :param guid: guid of the RM
+ :type guid: int
+ :param creds: Credentials to communicate with the rm
+ :type creds: dict
+
+ """
+
+ _rtype = "Tunnel"
+ _authorized_connections = ["OVSPort", "PlanetlabTap"]
+
+ @classmethod
+ def _register_attributes(cls):
+ """ Register the attributes of Connection RM
+
+ """
+ cipher = Attribute("cipher",
+ "Cipher to encript communication. "
+ "One of PLAIN, AES, Blowfish, DES, DES3. ",
+ default = None,
+ allowed = ["PLAIN", "AES", "Blowfish", "DES", "DES3"],
+ type = Types.Enumerate,
+ flags = Flags.ExecReadOnly)
+
+ cipher_key = Attribute("cipherKey",
+ "Specify a symmetric encryption key with which to protect "
+ "packets across the tunnel. python-crypto must be installed "
+ "on the system." ,
+ flags = Flags.ExecReadOnly)
+
+ txqueuelen = Attribute("txQueueLen",
+ "Specifies the interface's transmission queue length. "
+ "Defaults to 1000. ",
+ type = Types.Integer,
+ flags = Flags.ExecReadOnly)
+
+ bwlimit = Attribute("bwLimit",
+ "Specifies the interface's emulated bandwidth in bytes "
+ "per second.",
+ type = Types.Integer,
+ flags = Flags.ExecReadOnly)
+
+ cls._register_attribute(cipher)
+ cls._register_attribute(cipher_key)
+ cls._register_attribute(txqueuelen)
+ cls._register_attribute(bwlimit)
+
+ def __init__(self, ec, guid):
+ """
+ :param ec: The Experiment controller
+ :type ec: ExperimentController
+ :param guid: guid of the RM
+ :type guid: int
+
+ """
+ super(Tunnel, self).__init__(ec, guid)
+ self._home = "tunnel-%s" % self.guid
+ self.port_info_tunl = []
+ self._nodes = []
+ self._pid = None
+ self._ppid = None
+
+ @property
+ def node(self):
+ return self._nodes[0]
+
+ def app_home(self, node):
+ return os.path.join(node.exp_home, self._home)
+
+ def run_home(self, node):
+ return os.path.join(self.app_home(node), self.ec.run_id)
+
+ def port_endpoints(self):
+ # Switch-Switch connection
+ connected = []
+ for guid in self.connections:
+ rm = self.ec.get_resource(guid)
+ if hasattr(rm, "create_port"):
+ connected.append(rm)
+ return connected
+
+ def mixed_endpoints(self):
+ # Switch-Host connection
+ connected = [1, 2]
+ for guid in self.connections:
+ rm = self.ec.get_resource(guid)
+ if hasattr(rm, "create_port"):
+ connected[0] = rm
+ elif hasattr(rm, "udp_connect_command"):
+ connected[1] = rm
+ return connected
+
+ def get_node(self, endpoint):
+ # Get connected to the nodes
+ if hasattr(endpoint, "create_port"):
+ res = []
+ rm_list = endpoint.get_connected(OVSWitch.rtype())
+ if rm_list:
+ rm = rm_list[0].get_connected(PlanetlabNode.rtype())
+ if rm:
+ res.append(rm[0])
+ return res
+ else:
+ res = []
+ rm = endpoint.get_connected(PlanetlabNode.rtype())
+ if rm :
+ res.append(rm[0])
+ return res
+
+ @property
+ def endpoint1(self):
+ if self.check_endpoints():
+ port_endpoints = self.port_endpoints()
+ if port_endpoints: return port_endpoints[0]
+ else:
+ mixed_endpoints = self.mixed_endpoints()
+ if mixed_endpoints: return mixed_endpoints[0]
+
+ @property
+ def endpoint2(self):
+ if self.check_endpoints():
+ port_endpoints = self.port_endpoints()
+ if port_endpoints: return port_endpoints[1]
+ else:
+ mixed_endpoints = self.mixed_endpoints()
+ if mixed_endpoints: return mixed_endpoints[1]
+
+ def check_endpoints(self):
+ """ Check if the links are between switches
+ or switch-host. Return False for latter.
+ """
+ port_endpoints = self.port_endpoints()
+ if len(port_endpoints) == 2:
+ return True
+ else:
+ return False
+
+ def get_port_info(self, endpoint, rem_endpoint):
+ """ Retrieve the port_info list for each port
+
+ :param port_info_tunl: [hostname, publ_IP_addr, port_name,
+ virtual_ip, local_port_Numb]
+ :type port_info_tunl: list
+ """
+ self.port_info_tunl = []
+ if self.check_endpoints():
+ # Use for the link switch-->switch
+ self.port_info_tunl.append(endpoint.port_info)
+ host0, ip0, pname0, virt_ip0, pnumber0 = self.port_info_tunl[0]
+ self.port_info_tunl.append(rem_endpoint.port_info)
+ host1, ip1, pname1, virt_ip1, pnumber1 = self.port_info_tunl[1]
+ return (pname0, ip1, pnumber1)
+
+ else:
+ # Use for the link host-->switch
+ self.port_info_tunl.append(endpoint.port_info)
+ host0, ip0, pname0, virt_ip0, pnumber0 = self.port_info_tunl[0]
+ return pnumber0
+
+ def udp_connect(self, endpoint, rem_endpoint):
+ # Collect info from rem_endpoint
+ self._nodes = self.get_node(rem_endpoint)
+ remote_ip = socket.gethostbyname(self.node.get("hostname"))
+ # Collect info from endpoint
+ self._nodes = self.get_node(endpoint)
+ local_port_file = os.path.join(self.run_home(self.node),
+ "local_port")
+ remote_port_file = os.path.join(self.run_home(self.node),
+ "remote_port")
+ ret_file = os.path.join(self.run_home(self.node),
+ "ret_file")
+ cipher = self.get("cipher")
+ cipher_key = self.get("cipherKey")
+ bwlimit = self.get("bwLimit")
+ txqueuelen = self.get("txQueueLen")
+
+ rem_port = str(self.get_port_info(rem_endpoint, endpoint))
+ # Upload the remote port in a file
+ self.node.upload(rem_port,
+ remote_port_file,
+ text = True,
+ overwrite = False)
+
+ 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 host_connect.sh script
+ shfile = os.path.join(self.app_home(self.node), "host_connect.sh")
+ self.node.upload(udp_connect_command,
+ shfile,
+ text = True,
+ overwrite = False)
+
+ # invoke connect script
+ cmd = "bash %s" % shfile
+ (out, err), proc = self.node.run(cmd, self.run_home(self.node),
+ sudo = True,
+ stdout = "udp_stdout",
+ stderr = "udp_stderr")
+
+ # check if execution errors
+ msg = "Failed to connect endpoints"
+
+ if proc.poll():
+ self.fail()
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+ msg = "Connection on host %s configured" \
+ % self.node.get("hostname")
+ self.info(msg)
+
+ # Wait for pid file to be generated
+ self._nodes = self.get_node(endpoint)
+ pid, ppid = self.node.wait_pid(self.run_home(self.node))
+
+ # If the process is not running, check for error information
+ # on the remote machine
+ if not pid or not ppid:
+ (out, err), proc = self.node.check_errors(self.run_home(self.node))
+ # Out is what was written in the stderr file
+ if err:
+ self.fail()
+ msg = " Failed to start command '%s' " % command
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+
+ return (pid, ppid)
+
+ def switch_connect(self, endpoint, rem_endpoint):
+ """ Get switch connect command
+ """
+ # Get and configure switch connection command
+ (local_port_name, remote_ip, remote_port_num) = self.get_port_info(
+ endpoint, rem_endpoint)
+ switch_connect_command = endpoint.switch_connect_command(
+ local_port_name, remote_ip, remote_port_num)
+ self._nodes = self.get_node(endpoint)
+
+ # Upload command to the file sw_connect.sh
+ shfile = os.path.join(self.app_home(self.node), "sw_connect.sh")
+ self.node.upload(switch_connect_command,
+ shfile,
+ text = True,
+ overwrite = False)
+
+ #invoke connect script
+ cmd = "bash %s" % shfile
+ (out, err), proc = self.node.run(cmd, self.run_home(self.node),
+ sudo = True,
+ stdout = "sw_stdout",
+ stderr = "sw_stderr")
+
+ # check if execution errors occured
+ msg = "Failed to connect endpoints"
+
+ if proc.poll():
+ self.fail()
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+ else:
+ msg = "Connection on port %s configured" % local_port_name
+ self.info(msg)
+ return
+
+ def sw_host_connect(self, endpoint, rem_endpoint):
+ """Link switch--> host
+ """
+ # Retrieve remote port number from rem_endpoint
+ local_port_name = endpoint.get('port_name')
+ self._nodes = self.get_node(rem_endpoint)
+ time.sleep(2) # Without this, sometimes I get nothing in remote_port_num
+ remote_port_num = ''
+ (out, err), proc = self.node.check_output(self.run_home(self.node), 'local_port')
+ remote_port_num = int(out)
+ remote_ip = socket.gethostbyname(self.node.get("hostname"))
+ switch_connect_command = endpoint.switch_connect_command(
+ local_port_name, remote_ip, remote_port_num)
+
+ # Upload command to the file sw_connect.sh
+ self._nodes = self.get_node(endpoint)
+ shfile = os.path.join(self.app_home(self.node), "sw_connect.sh")
+ self.node.upload(switch_connect_command,
+ shfile,
+ text = True,
+ overwrite = False)
+
+ #invoke connect script
+ cmd = "bash %s" % shfile
+ (out, err), proc = self.node.run(cmd, self.run_home(self.node),
+ sudo = True,
+ stdout = "sw_stdout",
+ stderr = "sw_stderr")
+
+ # check if execution errors occured
+ msg = "Failed to connect endpoints"
+
+ if proc.poll():
+ self.fail()
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+ else:
+ msg = "Connection on port %s configured" % local_port_name
+ self.info(msg)
+ return
+
+ def provision(self):
+ """ Provision the tunnel
+ """
+ # Create folders
+ self._nodes = self.get_node(self.endpoint1)
+ self.node.mkdir(self.run_home(self.node))
+ self._nodes = self.get_node(self.endpoint2)
+ self.node.mkdir(self.run_home(self.node))
+
+ if self.check_endpoints():
+ #Invoke connect script between switches
+ switch_connect1 = self.switch_connect(self.endpoint1, self.endpoint2)
+ switch_connect2 = self.switch_connect(self.endpoint2, self.endpoint1)
+
+ else:
+ # Invoke connect script between switch & host
+ (self._pid, self._ppid) = self.udp_connect(self.endpoint2, self.endpoint1)
+ switch_connect = self.sw_host_connect(self.endpoint1, self.endpoint2)
+
+ self.debug("------- READY -------")
+ self._provision_time = tnow()
+ self._state = ResourceState.PROVISIONED
+
+ def discover(self):
+ """ Discover the tunnel
+
+ """
+ pass
+
+ def deploy(self):
+ if (not self.endpoint1 or self.endpoint1.state < ResourceState.READY) or \
+ (not self.endpoint2 or self.endpoint2.state < ResourceState.READY):
+ self.ec.schedule(reschedule_delay, self.deploy)
+ else:
+ try:
+ self.discover()
+ self.provision()
+ except:
+ self.fail()
+ raise
+
+ self.debug("----- READY ---- ")
+ self._ready_time = tnow()
+ self._state = ResourceState.READY
+
+ def start(self):
+ """ Start the RM. It means nothing special for
+ ovsport for now.
+ """
+ pass
+
+
+ def stop(self):
+ """ Stop the RM. It means nothing special for
+ ovsport for now.
+ """
+ pass
+
+ def release(self):
+ """ Release the udp_tunnel on endpoint2.
+ On endpoint1 means nothing special.
+ """
+ if not self.check_endpoints():
+ # Kill the TAP devices
+ # TODO: Make more generic Release method of PLTAP
+ if self._pid and self._ppid:
+ self._nodes = self.get_node(self.endpoint2)
+ (out, err), proc = self.node.kill(self._pid,
+ self._ppid, sudo = True)
+ if err or proc.poll():
+ # check if execution errors occurred
+ msg = " Failed to delete TAP device"
+ self.error(msg, err, err)
+ self.fail()
+
+ self._state = ResourceState.RELEASED
+
+
+
+
+
+
+
+
if self.state == ResourceState.STARTED:
self.info("Stopping command '%s'" % command)
-
+ self.info("STOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP")
command = "bash %s" % os.path.join(self.app_home, "stop.sh")
(out, err), proc = self.execute_command(command,
blocking = True)
if out.strip().find(self.get("deviceName")) == -1:
# tap is not running is not running (socket not found)
+ print "HEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
self._finish_time = tnow()
self._state = ResourceState.FINISHED
--- /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>
+# Alexandros Kouvakas <alexandros.kouvakas@gmail.com>
+
+# Switch1 ------- Switch2
+# / \
+# / \
+# / \
+# Host1 Host2
+
+from nepi.execution.ec import ExperimentController
+
+from test_utils import skipIfAnyNotAlive
+
+import os
+import time
+import unittest
+
+class OvsTestCase(unittest.TestCase):
+ def setUp(self):
+ self.switch1 = "planetlab2.virtues.fi"
+ self.switch2 = "planetlab2.upc.es"
+ self.host1 = "planetlab2.ionio.gr"
+ self.host2 = "planetlab2.cs.aueb.gr"
+ self.user = "inria_nepi"
+
+ @skipIfAnyNotAlive
+ def t_ovs(self, user1, switch1, user2, switch2, user3, host1, user4, host2):
+
+ ec = ExperimentController(exp_id = "test-ovs")
+
+ node1 = ec.register_resource("PlanetlabNode")
+ ec.set(node1, "hostname", switch1)
+ ec.set(node1, "username", user1)
+ ec.set(node1, "cleanHome", True)
+ ec.set(node1, "cleanProcesses", True)
+
+ ovs1 = ec.register_resource("OVSWitch")
+ ec.set(ovs1, "bridge_name", "nepi_bridge")
+ ec.set(ovs1, "virtual_ip_pref", "192.168.3.1/24")
+ ec.set(ovs1, "controller_ip", "85.23.168.77")
+ ec.set(ovs1, "controller_port", "6633")
+ ec.register_connection(ovs1, node1)
+
+ port1 = ec.register_resource("OVSPort")
+ ec.set(port1, "port_name", "port-1")
+ ec.register_connection(port1, ovs1)
+
+ port2 = ec.register_resource("OVSPort")
+ ec.set(port2, "port_name", "port-2")
+ ec.register_connection(port2, ovs1)
+
+ node2 = ec.register_resource("PlanetlabNode")
+ ec.set(node2, "hostname", switch2)
+ ec.set(node2, "username", user2)
+ ec.set(node2, "cleanHome", True)
+ ec.set(node2, "cleanProcesses", True)
+
+ ovs2 = ec.register_resource("OVSWitch")
+ ec.set(ovs2, "bridge_name", "nepi_bridge")
+ ec.set(ovs2, "virtual_ip_pref", "192.168.3.2/24")
+ ec.set(ovs2, "controller_ip", "85.23.168.77")
+ ec.set(ovs2, "controller_port", "6633")
+ ec.register_connection(ovs2, node2)
+
+ port3 = ec.register_resource("OVSPort")
+ ec.set(port3, "port_name", "port-3")
+ ec.register_connection(port3, ovs2)
+
+ port4 = ec.register_resource("OVSPort")
+ ec.set(port4, "port_name", "port-4")
+ ec.register_connection(port4, ovs2)
+
+ node3 = ec.register_resource("PlanetlabNode")
+ ec.set(node3, "hostname", host1)
+ ec.set(node3, "username", user3)
+ ec.set(node3, "cleanHome", True)
+ ec.set(node3, "cleanProcesses", True)
+
+ tap1 = ec.register_resource("PlanetlabTap")
+ ec.set(tap1, "ip4", "192.168.3.3")
+ ec.set(tap1, "pointopoint", "192.168.3.1")
+ ec.set(tap1, "prefix4", 24)
+ ec.register_connection(tap1, node3)
+
+ node4 = ec.register_resource("PlanetlabNode")
+ ec.set(node4, "hostname", host2)
+ ec.set(node4, "username", user4)
+ ec.set(node4, "cleanHome", True)
+ ec.set(node4, "cleanProcesses", True)
+
+ tap2 = ec.register_resource("PlanetlabTap")
+ ec.set(tap2, "ip4", "192.168.3.4")
+ ec.set(tap2, "pointopoint", "192.168.3.2")
+ ec.set(tap2, "prefix4", 24)
+ ec.register_connection(tap2, node4)
+
+ ovstun1 = ec.register_resource("Tunnel")
+ ec.register_connection(port1, ovstun1)
+ ec.register_connection(tap1, ovstun1)
+
+ ovstun2 = ec.register_resource("Tunnel")
+ ec.register_connection(port3, ovstun2)
+ ec.register_connection(tap2, ovstun2)
+
+ ovstun3 = ec.register_resource("Tunnel")
+ ec.register_connection(port2, ovstun3)
+ ec.register_connection(port4, ovstun3)
+
+ app1 = ec.register_resource("LinuxApplication")
+ cmd = "ping -c3 192.168.3.2"
+ ec.set(app1, "command", cmd)
+ ec.register_connection(app1, node1)
+
+ app2 = ec.register_resource("LinuxApplication")
+ cmd = "ping -c3 192.168.3.4"
+ ec.set(app2, "command", cmd)
+ ec.register_connection(app2, node2)
+
+ ec.deploy()
+
+ ec.wait_finished(app2)
+
+ if_name = ec.get(tap1, "deviceName")
+ self.assertTrue(if_name.startswith("tap"))
+
+ if_name = ec.get(tap2, "deviceName")
+ self.assertTrue(if_name.startswith("tap"))
+
+ ping1 = ec.trace(app1, 'stdout')
+ expected1 = """3 packets transmitted, 3 received, 0% packet loss"""
+ self.assertTrue(ping1.find(expected1) > -1)
+
+ ping2 = ec.trace(app2, 'stdout')
+ expected2 = """3 packets transmitted, 3 received, 0% packet loss"""
+ self.assertTrue(ping2.find(expected2) > -1)
+
+ ec.shutdown()
+
+ def test_ovs(self):
+ self.t_ovs(self.user, self.switch1, self.user, self.switch2, self.user, self.host1, self.user, self.host2)
+
+if __name__ == '__main__':
+ unittest.main()
+