From 4f61ec12520928b3403ee2a75c9e4ce1b30907a3 Mon Sep 17 00:00:00 2001 From: Julien Tribino Date: Thu, 11 Sep 2014 11:14:52 +0200 Subject: [PATCH] Delete the OVSTunnel to use the UdpTunnel instead. Just need to fix an issue with the linux tap : The test linux/udptunnel doesn't work --- examples/openvswitch/ovs_ping_2switches.py | 24 +- .../openvswitch/ovs_ping_3switches_line.py | 34 +- .../openvswitch/ovs_ping_3switches_loop.py | 40 +- src/nepi/resources/linux/tap.py | 86 +++- src/nepi/resources/linux/tunnel.py | 4 + src/nepi/resources/linux/udptunnel.py | 42 +- .../resources/planetlab/openvswitch/ovs.py | 9 +- .../planetlab/openvswitch/ovsport.py | 126 ++++-- .../resources/planetlab/openvswitch/tunnel.py | 391 ------------------ src/nepi/resources/planetlab/tap.py | 90 +++- test/resources/linux/udptunnel.py | 4 +- test/resources/planetlab/udptunnel.py | 3 +- 12 files changed, 323 insertions(+), 530 deletions(-) delete mode 100644 src/nepi/resources/planetlab/openvswitch/tunnel.py diff --git a/examples/openvswitch/ovs_ping_2switches.py b/examples/openvswitch/ovs_ping_2switches.py index 5b7a2086..47789117 100644 --- a/examples/openvswitch/ovs_ping_2switches.py +++ b/examples/openvswitch/ovs_ping_2switches.py @@ -55,9 +55,10 @@ def add_ovs(ec, bridge_name, virtual_ip_pref, controller_ip, controller_port, no ec.register_connection(ovs, node) return ovs -def add_port(ec, port_name, ovs): +def add_port(ec, port_name, network, ovs): port = ec.register_resource("OVSPort") ec.set(port, "port_name", port_name) + ec.set(port, "network", network) ec.register_connection(port, ovs) return port @@ -70,9 +71,8 @@ def add_tap(ec, endpoint_ip, endpoint_prefix, pointopoint, node): ec.register_connection(tap, node) return tap -def add_tunnel(ec, network, port0, tap): - tunnel = ec.register_resource("OVSTunnel") - ec.set(tunnel, "network", network) +def add_tunnel(ec, port0, tap): + tunnel = ec.register_resource("LinuxUdpTunnel") ec.register_connection(port0, tunnel) ec.register_connection(tunnel, tap) return tunnel @@ -92,7 +92,7 @@ switch2 = "planetlab2.upc.es" host1 = "planetlab2.ionio.gr" host2 = "iraplab2.iralab.uni-karlsruhe.de" -ip_controller = "xxx.yyy.zzz.ttt" +ip_controller = "1.1.1.1" #XXX : Depends on the Vsys_tag of your slice network = "192.168.3.0" @@ -111,10 +111,10 @@ ovs1 = add_ovs(ec, "nepi_bridge_1", "192.168.3.1/24", ip_controller, "6633", s1_ ovs2 = add_ovs(ec, "nepi_bridge_2", "192.168.3.2/24", ip_controller, "6633", s2_node) # Add ports on ovs -port1 = add_port(ec, "nepi_port1", ovs1) -port3 = add_port(ec, "nepi_port3", ovs1) -port2 = add_port(ec, "nepi_port2", ovs2) -port4 = add_port(ec, "nepi_port4", ovs2) +port1 = add_port(ec, "nepi_port1", network, ovs1) +port3 = add_port(ec, "nepi_port3", network, ovs1) +port2 = add_port(ec, "nepi_port2", network, ovs2) +port4 = add_port(ec, "nepi_port4", network, ovs2) h1_node = add_node(ec, host1, slicename, pl_user, pl_password) h2_node = add_node(ec, host2, slicename, pl_user, pl_password) @@ -124,9 +124,9 @@ tap1 = add_tap(ec, "192.168.3.3", 24, "192.168.3.1", h1_node) tap2 = add_tap(ec, "192.168.3.4", 24, "192.168.3.2", h2_node) # Connect the nodes -tunnel1 = add_tunnel(ec, network, port1, tap1) -tunnel2 = add_tunnel(ec, network, port2, tap2) -tunnel3 = add_tunnel(ec, network, port3, port4) +tunnel1 = add_tunnel(ec, port1, tap1) +tunnel2 = add_tunnel(ec, port2, tap2) +tunnel3 = add_tunnel(ec, port3, port4) # Add ping commands app1 = add_app(ec, "ping -c5 192.168.3.2", s1_node) diff --git a/examples/openvswitch/ovs_ping_3switches_line.py b/examples/openvswitch/ovs_ping_3switches_line.py index 0dd1783d..b48485ee 100644 --- a/examples/openvswitch/ovs_ping_3switches_line.py +++ b/examples/openvswitch/ovs_ping_3switches_line.py @@ -53,9 +53,10 @@ def add_ovs(ec, bridge_name, virtual_ip_pref, controller_ip, controller_port, no ec.register_connection(ovs, node) return ovs -def add_port(ec, port_name, ovs): +def add_port(ec, port_name, network, ovs): port = ec.register_resource("OVSPort") ec.set(port, "port_name", port_name) + ec.set(port, "network", network) ec.register_connection(port, ovs) return port @@ -68,9 +69,8 @@ def add_tap(ec, endpoint_ip, endpoint_prefix, pointopoint, node): ec.register_connection(tap, node) return tap -def add_tunnel(ec, network, port0, tap): - tunnel = ec.register_resource("OVSTunnel") - ec.set(tunnel, "network", network) +def add_tunnel(ec, port0, tap): + tunnel = ec.register_resource("LinuxUdpTunnel") ec.register_connection(port0, tunnel) ec.register_connection(tunnel, tap) return tunnel @@ -87,7 +87,7 @@ ec = ExperimentController(exp_id = "test-tr") #XXX : Need to put 6 working nodes or to let Nepi find for you switch1 = "planetlab2.virtues.fi" switch2 = "planetlab2.upc.es" -switch3 = "planetlab2.cs.aueb.gr" +switch3 = "planetlab1.informatik.uni-erlangen.de" host1 = "planetlab2.ionio.gr" host2 = "iraplab2.iralab.uni-karlsruhe.de" host3 = "planetlab2.diku.dk" @@ -113,13 +113,13 @@ ovs2 = add_ovs(ec, "nepi_bridge_2", "192.168.3.4/24", ip_controller, "6633", s2_ ovs3 = add_ovs(ec, "nepi_bridge_3", "192.168.3.6/24", ip_controller, "6633", s3_node) # Add ports on ovs -port1 = add_port(ec, "nepi_port1", ovs1) -port4 = add_port(ec, "nepi_port4", ovs1) -port7 = add_port(ec, "nepi_port7", ovs1) -port2 = add_port(ec, "nepi_port2", ovs2) -port5 = add_port(ec, "nepi_port5", ovs2) -port3 = add_port(ec, "nepi_port3", ovs3) -port6 = add_port(ec, "nepi_port6", ovs3) +port1 = add_port(ec, "nepi_port1", network, ovs1) +port4 = add_port(ec, "nepi_port4", network, ovs1) +port7 = add_port(ec, "nepi_port7", network, ovs1) +port2 = add_port(ec, "nepi_port2", network, ovs2) +port5 = add_port(ec, "nepi_port5", network, ovs2) +port3 = add_port(ec, "nepi_port3", network, ovs3) +port6 = add_port(ec, "nepi_port6", network, ovs3) h1_node = add_node(ec, host1, slicename, pl_user, pl_password) h2_node = add_node(ec, host2, slicename, pl_user, pl_password) @@ -131,11 +131,11 @@ tap2 = add_tap(ec, "192.168.3.3", 24, "192.168.3.4", h2_node) tap3 = add_tap(ec, "192.168.3.5", 24, "192.168.3.6", h3_node) # Connect the nodes -tunnel1 = add_tunnel(ec, network, port1, tap1) -tunnel2 = add_tunnel(ec, network, port2, tap2) -tunnel3 = add_tunnel(ec, network, port3, tap3) -tunnel4 = add_tunnel(ec, network, port4, port5) -tunnel5 = add_tunnel(ec, network, port7, port6) +tunnel1 = add_tunnel(ec, port1, tap1) +tunnel2 = add_tunnel(ec, port2, tap2) +tunnel3 = add_tunnel(ec, port3, tap3) +tunnel4 = add_tunnel(ec, port4, port5) +tunnel5 = add_tunnel(ec, port7, port6) #tunnel6 = add_tunnel(ec, network, port8, port9) # Add ping commands diff --git a/examples/openvswitch/ovs_ping_3switches_loop.py b/examples/openvswitch/ovs_ping_3switches_loop.py index 9544ba0d..7dac0956 100644 --- a/examples/openvswitch/ovs_ping_3switches_loop.py +++ b/examples/openvswitch/ovs_ping_3switches_loop.py @@ -63,9 +63,10 @@ def add_ovs(ec, bridge_name, virtual_ip_pref, controller_ip, controller_port, no ec.register_connection(ovs, node) return ovs -def add_port(ec, port_name, ovs): +def add_port(ec, port_name, network, ovs): port = ec.register_resource("OVSPort") ec.set(port, "port_name", port_name) + ec.set(port, "network", network) ec.register_connection(port, ovs) return port @@ -78,9 +79,8 @@ def add_tap(ec, endpoint_ip, endpoint_prefix, pointopoint, node): ec.register_connection(tap, node) return tap -def add_tunnel(ec, network, port0, tap): - tunnel = ec.register_resource("OVSTunnel") - ec.set(tunnel, "network", network) +def add_tunnel(ec, port0, tap): + tunnel = ec.register_resource("LinuxUdpTunnel") ec.register_connection(port0, tunnel) ec.register_connection(tunnel, tap) return tunnel @@ -97,7 +97,7 @@ ec = ExperimentController(exp_id = "test-tr") #XXX : Need to put 6 working nodes or to let Nepi find for you switch1 = "planetlab2.virtues.fi" switch2 = "planetlab2.upc.es" -switch3 = "planetlab2.cs.aueb.gr" +switch3 = "planetlab1.informatik.uni-erlangen.de" host1 = "planetlab2.ionio.gr" host2 = "iraplab2.iralab.uni-karlsruhe.de" host3 = "planetlab2.diku.dk" @@ -124,15 +124,15 @@ ovs2 = add_ovs(ec, "nepi_bridge_2", "192.168.3.4/24", ip_controller, "6633", s2_ ovs3 = add_ovs(ec, "nepi_bridge_3", "192.168.3.6/24", ip_controller, "6633", s3_node) # Add ports on ovs -port1 = add_port(ec, "nepi_port1", ovs1) -port4 = add_port(ec, "nepi_port4", ovs1) -port7 = add_port(ec, "nepi_port7", ovs1) -port2 = add_port(ec, "nepi_port2", ovs2) -port5 = add_port(ec, "nepi_port5", ovs2) -port8 = add_port(ec, "nepi_port8", ovs2) -port3 = add_port(ec, "nepi_port3", ovs3) -port6 = add_port(ec, "nepi_port6", ovs3) -port9 = add_port(ec, "nepi_port9", ovs3) +port1 = add_port(ec, "nepi_port1", network, ovs1) +port4 = add_port(ec, "nepi_port4", network, ovs1) +port7 = add_port(ec, "nepi_port7", network, ovs1) +port2 = add_port(ec, "nepi_port2", network, ovs2) +port5 = add_port(ec, "nepi_port5", network, ovs2) +port8 = add_port(ec, "nepi_port8", network, ovs2) +port3 = add_port(ec, "nepi_port3", network, ovs3) +port6 = add_port(ec, "nepi_port6", network, ovs3) +port9 = add_port(ec, "nepi_port9", network, ovs3) h1_node = add_node(ec, host1, slicename, pl_user, pl_password) h2_node = add_node(ec, host2, slicename, pl_user, pl_password) @@ -144,12 +144,12 @@ tap2 = add_tap(ec, "192.168.3.3", 24, "192.168.3.4", h2_node) tap3 = add_tap(ec, "192.168.3.5", 24, "192.168.3.6", h3_node) # Connect the nodes -tunnel1 = add_tunnel(ec, network, port1, tap1) -tunnel2 = add_tunnel(ec, network, port2, tap2) -tunnel3 = add_tunnel(ec, network, port3, tap3) -tunnel4 = add_tunnel(ec, network, port4, port5) -tunnel5 = add_tunnel(ec, network, port7, port6) -tunnel6 = add_tunnel(ec, network, port8, port9) +tunnel1 = add_tunnel(ec, port1, tap1) +tunnel2 = add_tunnel(ec, port2, tap2) +tunnel3 = add_tunnel(ec, port3, tap3) +tunnel4 = add_tunnel(ec, port4, port5) +tunnel5 = add_tunnel(ec, port7, port6) +tunnel6 = add_tunnel(ec, port8, port9) # Add ping commands app1 = add_app(ec, "ping -c5 192.168.3.4", s1_node) diff --git a/src/nepi/resources/linux/tap.py b/src/nepi/resources/linux/tap.py index 91640dff..b93ee800 100644 --- a/src/nepi/resources/linux/tap.py +++ b/src/nepi/resources/linux/tap.py @@ -283,7 +283,12 @@ class LinuxTap(LinuxApplication): return True - ## XXX: NOT REALLY WORKING YET! + def initiate_udp_connection(self, remote_endpoint, connection_app_home, + connection_run_home, cipher, cipher_key, bwlimit, txqueuelen): + port = self.udp_connect(remote_endpoint, connection_app_home, + connection_run_home, cipher, cipher_key, bwlimit, txqueuelen) + return port + def udp_connect(self, remote_endpoint, connection_app_home, connection_run_home, cipher, cipher_key, bwlimit, txqueuelen): udp_connect_command = self._udp_connect_command( @@ -291,14 +296,14 @@ class LinuxTap(LinuxApplication): cipher, cipher_key, bwlimit, txqueuelen) # upload command to connect.sh script - shfile = os.path.join(connection_app_home, "udp-connect.sh") + shfile = os.path.join(self.app_home, "udp-connect.sh") self.node.upload_command(udp_connect_command, shfile = shfile, overwrite = False) # invoke connect script cmd = "bash %s" % shfile - (out, err), proc = self.node.run(cmd, connection_run_home) + (out, err), proc = self.node.run(cmd, self.run_home) # check if execution errors occurred msg = "Failed to connect endpoints " @@ -308,19 +313,21 @@ class LinuxTap(LinuxApplication): raise RuntimeError, msg # Wait for pid file to be generated - pid, ppid = self.node.wait_pid(connection_run_home) + self._pid, self._ppid = self.node.wait_pid(self.run_home) # 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(connection_run_home) + if not self._pid or not self._ppid: + (out, err), proc = self.node.check_errors(self.run_home) # 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 - return pid, ppid + port = self.wait_local_port() + + return port def _udp_connect_command(self, remote_endpoint, connection_run_home, cipher, cipher_key, bwlimit, txqueuelen): @@ -335,13 +342,13 @@ class LinuxTap(LinuxApplication): remote_ip = remote_endpoint.node.get("ip") - local_port_file = os.path.join(connection_run_home, + local_port_file = os.path.join(self.run_home, "local_port") - remote_port_file = os.path.join(connection_run_home, + remote_port_file = os.path.join(self.run_home, "remote_port") - ret_file = os.path.join(connection_run_home, + ret_file = os.path.join(self.run_home, "ret_file") # Generate UDP connect command @@ -397,6 +404,65 @@ class LinuxTap(LinuxApplication): return command + def establish_udp_connection(self, remote_endpoint, port): + # upload remote port number to file + rem_port = "%s\n" % port + self.node.upload(rem_port, + os.path.join(self.run_home, "remote_port"), + text = True, + overwrite = False) + + def verify_connection(self): + self.wait_result() + + def terminate_connection(self): + if self._pid and self._ppid: + (out, err), proc = self.node.kill(self._pid, self._ppid, + sudo = True) + + # check if execution errors occurred + if proc.poll() and err: + msg = " Failed to Kill the Tap" + self.error(msg, out, err) + raise RuntimeError, msg + + def check_status(self): + return self.node.status(self._pid, self._ppid) + + def wait_local_port(self): + """ Waits until the local_port file for the endpoint is generated, + and returns the port number + + """ + return self.wait_file("local_port") + + def wait_result(self): + """ Waits until the return code file for the endpoint is generated + + """ + return self.wait_file("ret_file") + + def wait_file(self, filename): + """ Waits until file on endpoint is generated """ + result = None + delay = 1.0 + + for i in xrange(20): + (out, err), proc = self.node.check_output( + self.run_home, 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 + @property def _start_command(self): command = [] diff --git a/src/nepi/resources/linux/tunnel.py b/src/nepi/resources/linux/tunnel.py index 8c7402af..8e0760d9 100644 --- a/src/nepi/resources/linux/tunnel.py +++ b/src/nepi/resources/linux/tunnel.py @@ -86,6 +86,7 @@ class LinuxTunnel(LinuxApplication): self.endpoint1.node.mkdir(self.run_home(self.endpoint1)) self.endpoint2.node.mkdir(self.run_home(self.endpoint2)) + self.debug("Initiate the connection") # Start 2 step connection # Initiate connection from endpoint 1 to endpoint 2 data1 = self.initiate_connection(self.endpoint1, self.endpoint2) @@ -93,12 +94,14 @@ class LinuxTunnel(LinuxApplication): # Initiate connection from endpoint 2 to endpoint 1 data2 = self.initiate_connection(self.endpoint2, self.endpoint1) + self.debug("Establish the connection") # 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) + self.debug("Verify the connection") # check if connection was successful on both sides self.verify_connection(self.endpoint1, self.endpoint2) self.verify_connection(self.endpoint2, self.endpoint1) @@ -131,6 +134,7 @@ class LinuxTunnel(LinuxApplication): def do_stop(self): """ Stops application execution """ + if self.state == ResourceState.STARTED: self.info("Stopping tunnel") diff --git a/src/nepi/resources/linux/udptunnel.py b/src/nepi/resources/linux/udptunnel.py index 63ae086c..1643b904 100644 --- a/src/nepi/resources/linux/udptunnel.py +++ b/src/nepi/resources/linux/udptunnel.py @@ -84,7 +84,7 @@ class LinuxUdpTunnel(LinuxTunnel): connected = [] for guid in self.connections: rm = self.ec.get_resource(guid) - if hasattr(rm, "udp_connect"): + if hasattr(rm, "initiate_udp_connection"): connected.append(rm) return connected @@ -93,51 +93,33 @@ class LinuxUdpTunnel(LinuxTunnel): 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_app_home = self.app_home(endpoint) connection_run_home = self.run_home(endpoint) - pid, ppid = endpoint.udp_connect( + + port = endpoint.initiate_udp_connection( remote_endpoint, connection_app_home, connection_run_home, cipher, cipher_key, bwlimit, txqueuelen) - port = self.wait_local_port(endpoint) - - self._pids[endpoint] = (pid, ppid) - return port def establish_connection(self, endpoint, remote_endpoint, port): - self.upload_remote_port(endpoint, port) + endpoint.establish_udp_connection(remote_endpoint, port) def verify_connection(self, endpoint, remote_endpoint): - self.wait_result(endpoint) + endpoint.verify_connection() def terminate_connection(self, endpoint, remote_endpoint): - pid, ppid = self._pids[endpoint] - - if pid and ppid: - (out, err), proc = endpoint.node.kill(pid, ppid, - sudo = True) - - # check if execution errors occurred - if proc.poll() and err: - msg = " Failed to STOP tunnel" - self.error(msg, out, err) - raise RuntimeError, msg + endpoint.terminate_connection() 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) + status1 = self.endpoint1.check_status() + status2 = self.endpoint2.check_status() if status1 == ProcStatus.FINISHED and \ status2 == ProcStatus.FINISHED: @@ -191,11 +173,3 @@ class LinuxUdpTunnel(LinuxTunnel): 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) - diff --git a/src/nepi/resources/planetlab/openvswitch/ovs.py b/src/nepi/resources/planetlab/openvswitch/ovs.py index 3594a8c5..fb921e97 100644 --- a/src/nepi/resources/planetlab/openvswitch/ovs.py +++ b/src/nepi/resources/planetlab/openvswitch/ovs.py @@ -297,11 +297,12 @@ class OVSSwitch(LinuxApplication): """ from nepi.resources.planetlab.openvswitch.ovsport import OVSPort - rm = self.get_connected(OVSPort.get_rtype()) + rms = self.get_connected(OVSPort.get_rtype()) - if rm[0].state < ResourceState.RELEASED: - self.ec.schedule(reschedule_delay, self.release) - return + for rm in rms : + if rm.state < ResourceState.RELEASED: + self.ec.schedule(reschedule_delay, self.release) + return cmd = "sliver-ovs del-bridge %s" % self.get('bridge_name') (out, err), proc = self.node.run(cmd, self.ovs_checks, diff --git a/src/nepi/resources/planetlab/openvswitch/ovsport.py b/src/nepi/resources/planetlab/openvswitch/ovsport.py index 65713583..8275249f 100644 --- a/src/nepi/resources/planetlab/openvswitch/ovsport.py +++ b/src/nepi/resources/planetlab/openvswitch/ovsport.py @@ -26,6 +26,8 @@ from nepi.resources.planetlab.openvswitch.ovs import OVSSwitch from nepi.resources.planetlab.node import PlanetlabNode from nepi.resources.linux.application import LinuxApplication +import os + reschedule_delay = "0.5s" @clsinit_copy @@ -44,7 +46,7 @@ class OVSPort(LinuxApplication): _help = "Runs an OpenVSwitch on a PlanetLab host" _backend = "planetlab" - _authorized_connections = ["OVSSwitch", "OVSTunnel"] + _authorized_connections = ["OVSSwitch", "LinuxUdpTunnel", "LinuxTunnel"] @classmethod def _register_attributes(cls): @@ -53,8 +55,16 @@ class OVSPort(LinuxApplication): """ port_name = Attribute("port_name", "Name of the port", flags = Flags.Design) + endpoint_ip = Attribute("endpoint_ip", "IP of the endpoint. This is the attribute " + "you should use to establish a tunnel or a remote " + "connection between endpoint", + flags = Flags.Design) + network = Attribute("network", "Network used by the port", + flags = Flags.Design) cls._register_attribute(port_name) + cls._register_attribute(endpoint_ip) + cls._register_attribute(network) def __init__(self, ec, guid): """ @@ -65,8 +75,11 @@ class OVSPort(LinuxApplication): """ super(OVSPort, self).__init__(ec, guid) + + self._port_number = None - self.port_info = [] + # in case of connection by tunnel + self._remote_ip = None def log_message(self, msg): return " guid %d - OVSPort - %s " % (self.guid, msg) @@ -90,6 +103,10 @@ class OVSPort(LinuxApplication): if ovsswitch: return ovsswitch[0] return None + @property + def remote_ip(self): + return self._remote_ip + @property def port_number(self): return self._port_number @@ -136,12 +153,15 @@ class OVSPort(LinuxApplication): self.info("Created the port %s on switch %s" % (self.get('port_name'), self.ovsswitch.get('bridge_name'))) - def get_local_end(self): + def initiate_udp_connection(self, remote_endpoint, connection_app_home, + connection_run_home, cipher, cipher_key, bwlimit, txqueuelen): """ Get the local_endpoint of the port """ + self._remote_ip = remote_endpoint.node.get("ip") + msg = "Discovering the number of the port %s" % self.get('port_name') - self.debug(msg) + self.info(msg) command = "sliver-ovs get-local-endpoint %s" % self.get('port_name') out = err = "" @@ -164,39 +184,88 @@ class OVSPort(LinuxApplication): self.info("The number of the %s is %s" % (self.get('port_name'), self.port_number)) - - def set_port_info(self): - """ Set all the information about the port inside a list - """ - info = [] - info.append(self.node.get('hostname')) + if remote_endpoint.is_rm_instance("PlanetlabTap"): + self._vroute = self.ec.register_resource("PlanetlabVroute") + self.ec.set(self._vroute, "action", "add") + self.ec.set(self._vroute, "network", self.get("network")) + + print "Vroute Guid :" + str(self._vroute) + + self.ec.register_connection(self._vroute, remote_endpoint.guid) + self.ec.deploy(guids=[self._vroute], group = self.deployment_group) + + # For debugging + msg = "Route for the tap configured" + self.debug(msg) + + return self.port_number + - #Return the ip of the node - import socket - ip = socket.gethostbyname(self.node.get('hostname')) - info.append(ip) + def establish_udp_connection(self,remote_endpoint, port): + establish_connection_command = self._establish_connection_command(port) - info.append(self.get('port_name')) - info.append(self.ovsswitch.get('virtual_ip_pref')) - info.append(self.port_number) - return info + # upload command to connect.sh script + shfile = os.path.join(self.app_home, "sw-connect.sh") + self.node.upload_command(establish_connection_command, + shfile = shfile, + overwrite = False) - def switch_connect_command(self, local_port_name, - remote_ip, remote_port_num): + # invoke connect script + cmd = "bash %s" % shfile + (out, err), proc = self.node.run(cmd, self.run_home, + sudo = True, + stdout = "sw_stdout", + stderr = "sw_stderr") + + # 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 + self._pid, self._ppid = self.node.wait_pid(self.run_home) + + # If the process is not running, check for error information + # on the remote machine + if not self._pid or not self._ppid: + (out, err), proc = self.node.check_errors(self.run_home) + # 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 + + # For debugging + msg = "Connection on port configured" + self.debug(msg) + + + def _establish_connection_command(self, port): """ Script to create the connection from a switch to a remote endpoint """ + local_port_name = self.get('port_name') 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.append("%s " % self.remote_ip) + command.append("%s " % port) command = " ".join(command) command = self.replace_paths(command) return command + def verify_connection(self): + self.ovsswitch.ovs_status() + + def terminate_connection(self): + return True + + def check_status(self): + return self.node.status(self._pid, self._ppid) + def do_deploy(self): """ Deploy the OVS port after the OVS Switch """ @@ -210,25 +279,22 @@ class OVSPort(LinuxApplication): self.do_provision() self.create_port() - self.get_local_end() + end_ip = self.ovsswitch.get('virtual_ip_pref').split('/') + self.set("endpoint_ip", end_ip[0]) #Check the status of the OVS Switch self.ovsswitch.ovs_status() - # Save all the information inside a list - self.port_info = self.set_port_info() - super(OVSPort, self).do_deploy() def do_release(self): """ Delete the port on the OVSwitch. It needs to wait for the tunnel to be released. """ + from nepi.resources.linux.udptunnel import LinuxUdpTunnel + rm = self.get_connected(LinuxUdpTunnel.get_rtype()) - from nepi.resources.planetlab.openvswitch.tunnel import OVSTunnel - rm = self.get_connected(OVSTunnel.get_rtype()) - - if rm and rm[0].state < ResourceState.RELEASED: + if rm and rm[0].state < ResourceState.STOPPED: self.ec.schedule(reschedule_delay, self.release) return diff --git a/src/nepi/resources/planetlab/openvswitch/tunnel.py b/src/nepi/resources/planetlab/openvswitch/tunnel.py deleted file mode 100644 index 682d796e..00000000 --- a/src/nepi/resources/planetlab/openvswitch/tunnel.py +++ /dev/null @@ -1,391 +0,0 @@ -# -# 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 . -# -# Authors: Alina Quereilhac -# Alexandros Kouvakas -# Julien Tribino - - -from nepi.execution.attribute import Attribute, Flags, Types -from nepi.execution.resource import ResourceManager, ResourceFactory, clsinit_copy, \ - ResourceState -from nepi.resources.linux.application import LinuxApplication -from nepi.resources.planetlab.node import PlanetlabNode -from nepi.resources.planetlab.openvswitch.ovs import OVSSwitch -from nepi.util.timefuncs import tnow, tdiffsec -from nepi.resources.planetlab.vroute import PlanetlabVroute -from nepi.resources.planetlab.tap import PlanetlabTap - -import os -import time -import socket - -reschedule_delay = "0.5s" - -@clsinit_copy -class OVSTunnel(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 = "OVSTunnel" - _authorized_connections = ["OVSPort", "PlanetlabTap"] - - @classmethod - def _register_attributes(cls): - """ Register the attributes of OVSTunnel RM - - """ - network = Attribute("network", "IPv4 Network Address", - flags = Flags.Design) - - 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.Design) - - 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.Design) - - txqueuelen = Attribute("txQueueLen", - "Specifies the interface's transmission queue length. " - "Defaults to 1000. ", - type = Types.Integer, - flags = Flags.Design) - - bwlimit = Attribute("bwLimit", - "Specifies the interface's emulated bandwidth in bytes " - "per second.", - type = Types.Integer, - flags = Flags.Design) - - cls._register_attribute(network) - 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(OVSTunnel, self).__init__(ec, guid) - self._home = "tunnel-%s" % self.guid - self.port_info_tunl = [] - self._pid = None - self._ppid = None - self._vroute = None - self._node_endpoint1 = None - self._node_endpoint2 = None - - def log_message(self, msg): - return " guid %d - Tunnel - %s " % (self.guid, msg) - - 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) - - @property - def tap(self): - """ Return the Tap RM if it exists """ - rclass = ResourceFactory.get_resource_type(PlanetlabTap.get_rtype()) - for guid in self.connections: - rm = self.ec.get_resource(guid) - if isinstance(rm, rclass): - return rm - - @property - def ovsswitch(self): - """ Return the 1st switch """ - for guid in self.connections: - rm_port = self.ec.get_resource(guid) - if hasattr(rm_port, "create_port"): - rm_list = rm_port.get_connected(OVSSwitch.get_rtype()) - if rm_list: - return rm_list[0] - - @property - def check_switch_host_link(self): - """ Check if the links are between switches - or switch-host. Return False for the latter. - """ - if self.tap : - return True - return False - - - def endpoints(self): - """ Return the list with the two connected elements. - Either Switch-Switch or Switch-Host - """ - connected = [1, 1] - position = 0 - for guid in self.connections: - rm = self.ec.get_resource(guid) - if hasattr(rm, "create_port"): - connected[position] = rm - position += 1 - elif hasattr(rm, "udp_connect"): - connected[1] = rm - return connected - - def get_node(self, endpoint): - """ Get the nodes of the endpoint - """ - rm = [] - if hasattr(endpoint, "create_port"): - rm_list = endpoint.get_connected(OVSSwitch.get_rtype()) - if rm_list: - rm = rm_list[0].get_connected(PlanetlabNode.get_rtype()) - else: - rm = endpoint.get_connected(PlanetlabNode.get_rtype()) - - if rm : - return rm[0] - - @property - def endpoint1(self): - """ Return the first endpoint : Always a Switch - """ - endpoint = self.endpoints() - return endpoint[0] - - @property - def endpoint2(self): - """ Return the second endpoint : Either a Switch or a TAP - """ - endpoint = self.endpoints() - return endpoint[1] - - def get_port_info(self, endpoint1, endpoint2): - #TODO : Need to change it. Really bad to have method that return different type of things !!!!! - """ Retrieve the port_info list for each port - - """ - if self.check_switch_host_link : - host0, ip0, pname0, virt_ip0, pnumber0 = endpoint1.port_info - return pnumber0 - - host0, ip0, pname0, virt_ip0, pnumber0 = endpoint1.port_info - host1, ip1, pname1, virt_ip1, pnumber1 = endpoint2.port_info - - return pname0, ip1, pnumber1 - - def wait_local_port(self, node_endpoint): - """ Waits until the if_name file for the command is generated, - and returns the if_name for the device """ - - local_port = None - delay = 1.0 - - #TODO : Need to change it with reschedule to avoid the problem - # of the order of connection - for i in xrange(10): - (out, err), proc = node_endpoint.check_output(self.run_home(node_endpoint), 'local_port') - if out: - local_port = int(out) - break - else: - time.sleep(delay) - delay = delay * 1.5 - else: - msg = "Couldn't retrieve local_port" - self.error(msg, out, err) - raise RuntimeError, msg - - return local_port - - def connection(self, local_endpoint, rm_endpoint): - """ Create the connect command for each case : - - Host - Switch, - - Switch - Switch, - - Switch - Host - """ - local_node = self.get_node(local_endpoint) - local_node.mkdir(self.run_home(local_node)) - - rm_node = self.get_node(rm_endpoint) - rm_node.mkdir(self.run_home(rm_node)) - - # Host to switch - if self.check_switch_host_link and local_endpoint == self.endpoint2 : - # Collect info from rem_endpoint - remote_ip = socket.gethostbyname(rm_node.get("hostname")) - - # Collect info from endpoint - connection_run_home = self.run_home(local_node) - connection_app_home = self.app_home(local_node) - cipher = self.get("cipher") - cipher_key = self.get("cipherKey") - bwlimit = self.get("bwLimit") - txqueuelen = self.get("txQueueLen") - - - # Upload the remote port in a file - rem_port = str(self.get_port_info(rm_endpoint,local_endpoint)) - rem_port_file = os.path.join(self.run_home(local_node), "remote_port") - local_node.upload(rem_port, rem_port_file, - text = True, - overwrite = False) - - self._pid, self._ppid = local_endpoint.udp_connect( - rm_node, connection_run_home, connection_app_home, - cipher, cipher_key, bwlimit, txqueuelen) - - -# connect_command = local_endpoint.udp_connect_command( -# remote_ip, local_port_file, rem_port_file, -# ret_file, cipher, cipher_key, bwlimit, txqueuelen) - -# self.connection_command(connect_command, local_node, rm_node) - -# # Wait for pid file to be generated -# self._pid, self._ppid = local_node.wait_pid(self.run_home(local_node)) - - if not self._pid or not self._ppid: - (out, err), proc = local_node.check_errors(self.run_home(local_node)) - # Out is what was written in the stderr file - if err: - msg = " Failed to start connection of the OVS Tunnel " - self.error(msg, out, err) - raise RuntimeError, msg - return - - # Switch to Host - if self.check_switch_host_link and local_endpoint == self.endpoint1: - local_port_name = local_endpoint.get('port_name') - remote_port_num = self.wait_local_port(rm_node) - remote_ip = socket.gethostbyname(rm_node.get("hostname")) - - # Switch to Switch - if not self.check_switch_host_link : - local_port_name, remote_ip, remote_port_num = self.get_port_info(local_endpoint, rm_endpoint) - - connect_command = local_endpoint.switch_connect_command( - local_port_name, remote_ip, remote_port_num) - - self.connection_command(connect_command, local_node, rm_node) - - def connection_command(self, command, node_endpoint, rm_node_endpoint): - """ Execute the connection command on the node and check if the processus is - correctly running on the node. - """ - shfile = os.path.join(self.app_home(node_endpoint), "sw_connect.sh") - node_endpoint.upload(command, - shfile, - text = True, - overwrite = False) - - # Invoke connect script - out = err= '' - cmd = "bash %s" % shfile - (out, err), proc = node_endpoint.run(cmd, self.run_home(node_endpoint), - sudo = True, - stdout = "sw_stdout", - stderr = "sw_stderr") - - # Check if execution errors occured - - if proc.poll(): - msg = "Failed to connect endpoints" - self.error(msg, out, err) - raise RuntimeError, msg - - # For debugging - msg = "Connection on port configured" - self.debug(msg) - - def do_provision(self): - """ Provision the tunnel - """ - - #TODO : The order of the connection is important for now ! - # Need to change the code of wait local port - self.connection(self.endpoint2, self.endpoint1) - self.connection(self.endpoint1, self.endpoint2) - - def configure_route(self): - """ Configure the route for the tap device - - .. note : In case of a conection between a switch and a host, a route - was missing on the node with the Tap Device. This method create - the missing route. - """ - - if self.check_switch_host_link: - self._vroute = self.ec.register_resource("PlanetlabVroute") - self.ec.set(self._vroute, "action", "add") - self.ec.set(self._vroute, "network", self.get("network")) - - self.ec.register_connection(self._vroute, self.tap.guid) - self.ec.deploy(guids=[self._vroute], group = self.deployment_group) - - def do_deploy(self): - """ Deploy the tunnel after the endpoint get ready - """ - 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) - return - - self.do_discover() - self.do_provision() - self.configure_route() - - # Cannot call the deploy of the linux application - # because of a log error. - # Need to investigate if it is right that the tunnel - # inherits from the linux application - # super(OVSTunnel, self).do_deploy() - self.set_ready() - - def do_release(self): - """ Release the tunnel by releasing the Tap Device if exists - """ - if self.check_switch_host_link: - # TODO: Make more generic Release method of PLTAP - tap_node = self.get_node(self.endpoint2) - if self._pid and self._ppid: - (out, err), proc = tap_node.kill(self._pid, - self._ppid, sudo = True) - - if err or proc.poll(): - msg = " Failed to delete TAP device" - self.error(msg, out, err) - - super(OVSTunnel, self).do_release() - diff --git a/src/nepi/resources/planetlab/tap.py b/src/nepi/resources/planetlab/tap.py index 5a47663c..4df138bb 100644 --- a/src/nepi/resources/planetlab/tap.py +++ b/src/nepi/resources/planetlab/tap.py @@ -343,6 +343,14 @@ class PlanetlabTap(LinuxApplication): return True + + def initiate_udp_connection(self, remote_endpoint, connection_app_home, + connection_run_home, cipher, cipher_key, bwlimit, txqueuelen): + port = self.udp_connect(remote_endpoint, connection_app_home, + connection_run_home, cipher, cipher_key, bwlimit, txqueuelen) + return port + + def udp_connect(self, remote_endpoint, connection_app_home, connection_run_home, cipher, cipher_key, bwlimit, txqueuelen): udp_connect_command = self._udp_connect_command( @@ -350,14 +358,14 @@ class PlanetlabTap(LinuxApplication): cipher, cipher_key, bwlimit, txqueuelen) # upload command to connect.sh script - shfile = os.path.join(connection_app_home, "udp-connect.sh") + shfile = os.path.join(self.app_home, "udp-connect.sh") self.node.upload_command(udp_connect_command, shfile = shfile, overwrite = False) # invoke connect script cmd = "bash %s" % shfile - (out, err), proc = self.node.run(cmd, connection_run_home) + (out, err), proc = self.node.run(cmd, self.run_home) # check if execution errors occurred msg = "Failed to connect endpoints " @@ -367,19 +375,21 @@ class PlanetlabTap(LinuxApplication): raise RuntimeError, msg # Wait for pid file to be generated - pid, ppid = self.node.wait_pid(connection_run_home) + self._pid, self._ppid = self.node.wait_pid(self.run_home) # 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(connection_run_home) + if not self._pid or not self._ppid: + (out, err), proc = self.node.check_errors(self.run_home) # 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 - return pid, ppid + port = self.wait_local_port() + + return port def _udp_connect_command(self, remote_endpoint, connection_run_home, cipher, cipher_key, bwlimit, txqueuelen): @@ -389,13 +399,13 @@ class PlanetlabTap(LinuxApplication): remote_ip = remote_endpoint.node.get("ip") - local_port_file = os.path.join(connection_run_home, + local_port_file = os.path.join(self.run_home, "local_port") - remote_port_file = os.path.join(connection_run_home, + remote_port_file = os.path.join(self.run_home, "remote_port") - ret_file = os.path.join(connection_run_home, + ret_file = os.path.join(self.run_home, "ret_file") # Generate UDP connect command @@ -432,6 +442,67 @@ class PlanetlabTap(LinuxApplication): return command + def establish_udp_connection(self, remote_endpoint, port): + # upload remote port number to file + rem_port = "%s\n" % port + self.node.upload(rem_port, + os.path.join(self.run_home, "remote_port"), + text = True, + overwrite = False) + + def verify_connection(self): + self.wait_result() + + def terminate_connection(self): + if self._pid and self._ppid: + (out, err), proc = self.node.kill(self._pid, self._ppid, + sudo = True) + + # check if execution errors occurred + if proc.poll() and err: + msg = " Failed to Kill the Tap" + self.error(msg, out, err) + raise RuntimeError, msg + + def check_status(self): + return self.node.status(self._pid, self._ppid) + + def wait_local_port(self): + """ Waits until the local_port file for the endpoint is generated, + and returns the port number + + """ + return self.wait_file("local_port") + + def wait_result(self): + """ Waits until the return code file for the endpoint is generated + + """ + return self.wait_file("ret_file") + + def wait_file(self, filename): + """ Waits until file on endpoint is generated """ + result = None + delay = 1.0 + + for i in xrange(20): + (out, err), proc = self.node.check_output( + self.run_home, 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 _gre_connect_command(self, remote_endpoint, connection_run_home): # Set the remote endpoint self.set("pointopoint", remote_endpoint.get("endpoint_ip")) @@ -456,6 +527,7 @@ class PlanetlabTap(LinuxApplication): return command + @property def _start_command(self): if self.gre_enabled: diff --git a/test/resources/linux/udptunnel.py b/test/resources/linux/udptunnel.py index fa93c3c4..24ddf149 100755 --- a/test/resources/linux/udptunnel.py +++ b/test/resources/linux/udptunnel.py @@ -144,11 +144,11 @@ class LinuxUdpTunnelTestCase(unittest.TestCase): ec.shutdown() - def ztest_tap_udp_tunnel(self): + def test_tap_udp_tunnel(self): self.t_tap_udp_tunnel(self.user1, self.host1, self.identity, self.user2, self.host2, self.identity) - def test_tun_udp_tunnel(self): + def ztest_tun_udp_tunnel(self): self.t_tun_udp_tunnel(self.user1, self.host1, self.identity, self.user2, self.host2, self.identity) diff --git a/test/resources/planetlab/udptunnel.py b/test/resources/planetlab/udptunnel.py index 1254d19a..e54c3efe 100755 --- a/test/resources/planetlab/udptunnel.py +++ b/test/resources/planetlab/udptunnel.py @@ -34,7 +34,8 @@ class PlanetlabUdpTunnelTestCase(unittest.TestCase): self.host1 = "planetlab1.informatik.uni-erlangen.de" self.host2 = "planck227ple.test.ibbt.be" self.user = "inria_nepi" - self.identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME']) + #self.identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME']) + self.identity = "%s/.ssh/id_rsa" % (os.environ['HOME']) #self.netblock = "192.168.1" self.netblock = "192.168.3" -- 2.43.0