X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=src%2Fnepi%2Fresources%2Fplanetlab%2Fopenvswitch%2Ftunnel.py;h=682d796e64a184392240f8b21f4b2ac0522b8500;hb=38575a0f27f26511965771dde8320060b10252dd;hp=c9c27119376677aac4eb0b63b6ed81f61711f0fb;hpb=c2acc774e82685942698b64e8ca7b4a27200e5dc;p=nepi.git diff --git a/src/nepi/resources/planetlab/openvswitch/tunnel.py b/src/nepi/resources/planetlab/openvswitch/tunnel.py index c9c27119..682d796e 100644 --- a/src/nepi/resources/planetlab/openvswitch/tunnel.py +++ b/src/nepi/resources/planetlab/openvswitch/tunnel.py @@ -15,8 +15,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# Author: Alina Quereilhac -# Alexandros Kouvakas +# Authors: Alina Quereilhac +# Alexandros Kouvakas +# Julien Tribino from nepi.execution.attribute import Attribute, Flags, Types @@ -24,7 +25,7 @@ from nepi.execution.resource import ResourceManager, ResourceFactory, clsinit_co 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.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 @@ -54,11 +55,11 @@ class OVSTunnel(LinuxApplication): @classmethod def _register_attributes(cls): - """ Register the attributes of Connection RM + """ Register the attributes of OVSTunnel RM """ network = Attribute("network", "IPv4 Network Address", - flags = Flags.ExecReadOnly) + flags = Flags.Design) cipher = Attribute("cipher", "Cipher to encript communication. " @@ -66,25 +67,25 @@ class OVSTunnel(LinuxApplication): default = None, allowed = ["PLAIN", "AES", "Blowfish", "DES", "DES3"], type = Types.Enumerate, - flags = Flags.ExecReadOnly) + 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.ExecReadOnly) + flags = Flags.Design) txqueuelen = Attribute("txQueueLen", "Specifies the interface's transmission queue length. " "Defaults to 1000. ", type = Types.Integer, - flags = Flags.ExecReadOnly) + flags = Flags.Design) bwlimit = Attribute("bwLimit", "Specifies the interface's emulated bandwidth in bytes " "per second.", type = Types.Integer, - flags = Flags.ExecReadOnly) + flags = Flags.Design) cls._register_attribute(network) cls._register_attribute(cipher) @@ -120,7 +121,7 @@ class OVSTunnel(LinuxApplication): @property def tap(self): - ''' Return the Tap RM if it exists ''' + """ 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) @@ -128,29 +129,29 @@ class OVSTunnel(LinuxApplication): return rm @property - def ovswitch(self): - ''' Return the 1st switch ''' + 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(OVSWitch.get_rtype()) + 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 latter. - ''' + """ 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. + """ Return the list with the two connected elements. Either Switch-Switch or Switch-Host - ''' + """ connected = [1, 1] position = 0 for guid in self.connections: @@ -158,36 +159,16 @@ class OVSTunnel(LinuxApplication): if hasattr(rm, "create_port"): connected[position] = rm position += 1 - elif hasattr(rm, "udp_connect_command"): + elif hasattr(rm, "udp_connect"): connected[1] = rm return connected -# 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 + """ Get the nodes of the endpoint + """ rm = [] if hasattr(endpoint, "create_port"): - rm_list = endpoint.get_connected(OVSWitch.get_rtype()) + rm_list = endpoint.get_connected(OVSSwitch.get_rtype()) if rm_list: rm = rm_list[0].get_connected(PlanetlabNode.get_rtype()) else: @@ -198,26 +179,20 @@ class OVSTunnel(LinuxApplication): @property def endpoint1(self): - endpoint = self.endpoints() - return endpoint[0] + """ Return the first endpoint : Always a Switch + """ + endpoint = self.endpoints() + return endpoint[0] @property def endpoint2(self): - endpoint = self.endpoints() - return endpoint[1] - -# @property -# 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 -# return False + """ Return the second endpoint : Either a Switch or a TAP + """ + endpoint = self.endpoints() + return endpoint[1] def get_port_info(self, endpoint1, endpoint2): - # Need to change it. Not good to have method that return different type of things !!!!! + #TODO : Need to change it. Really bad to have method that return different type of things !!!!! """ Retrieve the port_info list for each port """ @@ -230,111 +205,17 @@ class OVSTunnel(LinuxApplication): return pname0, ip1, pnumber1 - def host_to_switch_connect(self, tap_endpoint, sw_endpoint): - # Collect info from rem_endpoint - remote_ip = socket.gethostbyname(self.node_endpoint1.get("hostname")) - - # Collect info from endpoint - local_port_file = os.path.join(self.run_home(self.node_endpoint2), "local_port") - rem_port_file = os.path.join(self.run_home(self.node_endpoint2), "remote_port") - ret_file = os.path.join(self.run_home(self.node_endpoint2), "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( sw_endpoint,tap_endpoint)) - - # Upload the remote port in a file - self.node_endpoint2.upload(rem_port, rem_port_file, - text = True, - overwrite = False) - - udp_connect_command = tap_endpoint.udp_connect_command( - remote_ip, local_port_file, rem_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_endpoint2), "host_connect.sh") - self.node_endpoint2.upload(udp_connect_command, shfile, - text = True, - overwrite = False) - - # invoke connect script - cmd = "bash %s" % shfile - (out, err), proc = self.node_endpoint2.run(cmd, self.run_home(self.node_endpoint2), - sudo = True, - stdout = "udp_stdout", - stderr = "udp_stderr") - - # check if execution errors - if proc.poll(): - msg = "Failed to connect endpoints" - self.error(msg, out, err) - raise RuntimeError, msg - - msg = "Connection on host %s configured" % self.node_endpoint2.get("hostname") - self.debug(msg) - - # Wait for pid file to be generated - pid, ppid = self.node_endpoint2.wait_pid(self.run_home(self.node_endpoint2)) - - # 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_endpoint2.check_errors(self.run_home(self.node_endpoint2)) - # 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) - - def switch_to_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) - node_endpoint = self.get_node(endpoint) - - # Upload command to the file sw_connect.sh - shfile = os.path.join(self.app_home(node_endpoint), "sw_connect.sh") - node_endpoint.upload(switch_connect_command, - shfile, - text = True, - overwrite = False) - - #invoke connect script - 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 %s configured" % local_port_name - self.info(msg) - - def wait_local_port(self): + 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 = self.node_endpoint2.check_output(self.run_home(self.node_endpoint2), 'local_port') + (out, err), proc = node_endpoint.check_output(self.run_home(node_endpoint), 'local_port') if out: local_port = int(out) break @@ -348,28 +229,91 @@ class OVSTunnel(LinuxApplication): return local_port - def switch_to_host_connect(self, sw_endpoint, host_endpoint): - """Link switch--> host + 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. """ - # Retrieve remote port number from sw_endpoint - local_port_name = sw_endpoint.get('port_name') - - out = err= '' - remote_port_num = self.wait_local_port() - remote_ip = socket.gethostbyname(self.node_endpoint2.get("hostname")) - switch_connect_command = sw_endpoint.switch_connect_command( - local_port_name, remote_ip, remote_port_num) - - # Upload command to the file sw_connect.sh - shfile = os.path.join(self.app_home(self.node_endpoint1), "sw_connect.sh") - self.node_endpoint1.upload(switch_connect_command, + 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 = self.node_endpoint1.run(cmd, self.run_home(self.node_endpoint1), + (out, err), proc = node_endpoint.run(cmd, self.run_home(node_endpoint), sudo = True, stdout = "sw_stdout", stderr = "sw_stderr") @@ -382,44 +326,37 @@ class OVSTunnel(LinuxApplication): raise RuntimeError, msg # For debugging - msg = "Connection on port %s configured" % local_port_name - self.debug(msg) + msg = "Connection on port configured" + self.debug(msg) def do_provision(self): """ Provision the tunnel - - ..note : Endpoint 1 is always a OVSPort. - Endpoint 2 can be either a OVSPort or a Tap - """ - self.node_endpoint1 = self.get_node(self.endpoint1) - self.node_endpoint1.mkdir(self.run_home(self.node_endpoint1)) - - self.node_endpoint2 = self.get_node(self.endpoint2) - self.node_endpoint2.mkdir(self.run_home(self.node_endpoint2)) + + #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) - if not self.check_switch_host_link: - # Invoke connect script between switches - self.switch_to_switch_connect(self.endpoint1, self.endpoint2) - self.switch_to_switch_connect(self.endpoint2, self.endpoint1) - else: - # Invoke connect script between switch & host - (self._pid, self._ppid) = self.host_to_switch_connect(self.endpoint2, self.endpoint1) - self.switch_to_host_connect(self.endpoint1, self.endpoint2) + def configure_route(self): + """ Configure the route for the tap device - #super(OVSTunnel, self).do_provision() + .. 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. + """ - def configure(self): 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) - # schedule deploy 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) @@ -427,20 +364,23 @@ class OVSTunnel(LinuxApplication): self.do_discover() self.do_provision() - self.configure() + 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() - #super(OVSTunnel, self).do_deploy() def do_release(self): - """ Release the udp_tunnel on endpoint2. - On endpoint1 means nothing special. + """ Release the tunnel by releasing the Tap Device if exists """ - if not self.check_switch_host_link: - # Kill the TAP devices + 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 = self.node_enpoint2.kill(self._pid, + (out, err), proc = tap_node.kill(self._pid, self._ppid, sudo = True) if err or proc.poll():