Merging the openflow part to the nepi-3-dev branch
[nepi.git] / src / nepi / resources / planetlab / openvswitch / ovsport.py
1 #
2 #    NEPI, a framework to manage network experiments
3 #    Copyright (C) 2013 INRIA
4 #
5 #    This program is free software: you can redistribute it and/or modify
6 #    it under the terms of the GNU General Public License as published by
7 #    the Free Software Foundation, either version 3 of the License, or
8 #    (at your option) any later version.
9 #
10 #    This program is distributed in the hope that it will be useful,
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #    GNU General Public License for more details.
14 #
15 #    You should have received a copy of the GNU General Public License
16 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
19 #             Alexandros Kouvakas <alexandros.kouvakas@gmail.com>
20
21 from nepi.execution.attribute import Attribute, Flags, Types
22 from nepi.execution.resource import ResourceManager, clsinit_copy, \
23         ResourceState
24 from nepi.resources.planetlab.openvswitch.ovs import OVSSwitch        
25 from nepi.resources.planetlab.node import PlanetlabNode        
26 from nepi.resources.linux.application import LinuxApplication
27
28 reschedule_delay = "0.5s"
29
30 @clsinit_copy                 
31 class OVSPort(LinuxApplication):
32     """
33     .. class:: Class Args :
34       
35         :param ec: The Experiment controller
36         :type ec: ExperimentController
37         :param guid: guid of the RM
38         :type guid: int
39
40     """
41     
42     _rtype = "OVSPort"
43     _help = "Runs an OpenVSwitch on a PlanetLab host"
44     _backend = "planetlab"
45
46     _authorized_connections = ["OVSSwitch", "OVSTunnel"]      
47
48     @classmethod
49     def _register_attributes(cls):
50         """ Register the attributes of OVSPort RM 
51
52         """
53         port_name = Attribute("port_name", "Name of the port",
54             flags = Flags.Design)                       
55
56         cls._register_attribute(port_name)
57
58     def __init__(self, ec, guid):
59         """
60         :param ec: The Experiment controller
61         :type ec: ExperimentController
62         :param guid: guid of the RM
63         :type guid: int
64     
65         """
66         super(OVSPort, self).__init__(ec, guid)
67         self._port_number = None
68         self.port_info = []          
69
70     def log_message(self, msg):
71         return " guid %d - OVSPort - %s " % (self.guid, msg)
72
73     @property
74     def node(self):
75         """ Node that run the switch and the ports
76         """
77         rm_list = self.get_connected(OVSSwitch.get_rtype())
78         if rm_list:
79             for elt in rm_list:
80                 node = elt.get_connected(PlanetlabNode.get_rtype())
81                 if node: return node[0]
82         return node[0]
83
84     @property
85     def ovsswitch(self):
86         """ Switch where the port is created
87         """
88         ovsswitch = self.get_connected(OVSSwitch.get_rtype())
89         if ovsswitch: return ovsswitch[0]
90         return None
91         
92     @property
93     def port_number(self):
94         return self._port_number
95
96     def valid_connection(self, guid):
97         """ Check if the connection is available.
98
99         :param guid: Guid of the current RM
100         :type guid: int
101         :rtype:  Boolean
102
103         """
104         rm = self.ec.get_resource(guid)
105         if rm.get_rtype() in self._authorized_connections:
106             msg = "Connection between %s %s and %s %s accepted" % (self.get_rtype(), self._guid, rm.get_rtype(), guid)
107             self.debug(msg)
108             return True
109         msg = "Connection between %s %s and %s %s refused" % (self.get_rtype(), self._guid, rm.get_rtype(), guid)
110         self.debug(msg)
111
112     def create_port(self):
113         """ Create the desired port
114         """
115         msg = "Creating the port %s" % self.get('port_name')
116         self.debug(msg)
117
118         if not self.get('port_name'):
119             msg = "The port name is not assigned"
120             self.error(msg)
121             raise AttributeError, msg
122
123         if not self.ovsswitch:
124             msg = "The OVSwitch RM is not running"
125             self.error(msg)
126             raise AttributeError, msg
127
128         cmd = "sliver-ovs create-port %s %s" % (self.ovsswitch.get('bridge_name'),
129                                                 self.get('port_name'))   
130         self.node.run(cmd, self.ovsswitch.ovs_checks, 
131                 stderr = "stdout-%s" % self.get('port_name'), 
132                 stdout = "stderr-%s" % self.get('port_name'),
133                 sudo = True)
134
135         self.info("Created the port %s on switch %s" % (self.get('port_name'),
136                                              self.ovsswitch.get('bridge_name')))     
137             
138     def get_local_end(self):
139         """ Get the local_endpoint of the port
140         """
141
142         msg = "Discovering the number of the port %s" % self.get('port_name')
143         self.debug(msg)
144
145         command = "sliver-ovs get-local-endpoint %s" % self.get('port_name')
146         out = err = ""
147         (out, err), proc = self.node.run_and_wait(command, 
148                 self.ovsswitch.ovs_checks,
149                 shfile = "port_number-%s.sh" % self.get('port_name'),
150                 pidfile = "port_number_pidfile-%s" % self.get('port_name'),
151                 ecodefile = "port_number_exitcode-%s" % self.get('port_name'), 
152                 sudo = True, 
153                 stdout = "stdout-%s" % self.get('port_name'),    
154                 stderr = "stderr-%s" % self.get('port_name'))
155
156         if err != "":
157             msg = "Error retrieving the local endpoint of the port"
158             self.error(msg)
159             raise AttributeError, msg
160
161         if out:
162             self._port_number = int(out)
163
164         self.info("The number of the %s is %s" % (self.get('port_name'), 
165            self.port_number))
166    
167     def set_port_info(self):
168         """ Set all the information about the port inside a list
169         """
170
171         info = []
172         info.append(self.node.get('hostname'))
173
174         #Return the ip of the node
175         import socket
176         ip = socket.gethostbyname(self.node.get('hostname'))
177         info.append(ip)
178
179         info.append(self.get('port_name'))
180         info.append(self.ovsswitch.get('virtual_ip_pref'))
181         info.append(self.port_number)
182         return info
183
184     def switch_connect_command(self, local_port_name, 
185             remote_ip, remote_port_num):
186         """ Script to create the connection from a switch to a 
187              remote endpoint
188         """
189
190         command = ["sliver-ovs"]
191         command.append("set-remote-endpoint ")
192         command.append("%s " % local_port_name)
193         command.append("%s " % remote_ip)
194         command.append("%s " % remote_port_num)
195         command = " ".join(command)
196         command = self.replace_paths(command)
197         return command
198         
199     def do_deploy(self):
200         """ Deploy the OVS port after the OVS Switch
201         """
202
203         if not self.ovsswitch or self.ovsswitch.state < ResourceState.READY:       
204             self.debug("---- RESCHEDULING DEPLOY ---- OVSwitch state %s " % self.ovsswitch.state )  
205             self.ec.schedule(reschedule_delay, self.deploy)
206             return
207
208         self.do_discover()
209         self.do_provision()
210
211         self.create_port()
212         self.get_local_end()
213
214         #Check the status of the OVS Switch
215         self.ovsswitch.ovs_status()
216
217         # Save all the information inside a list
218         self.port_info = self.set_port_info()
219
220         super(OVSPort, self).do_deploy()
221
222     def do_release(self):
223         """ Delete the port on the OVSwitch. It needs to wait for the tunnel
224         to be released.
225         """
226
227         from nepi.resources.planetlab.openvswitch.tunnel import OVSTunnel
228         rm = self.get_connected(OVSTunnel.get_rtype())
229
230         if rm and rm[0].state < ResourceState.RELEASED:
231             self.ec.schedule(reschedule_delay, self.release)
232             return 
233             
234         cmd = "sliver-ovs del_port %s" % self.get('port_name')
235         (out, err), proc = self.node.run(cmd, self.ovsswitch.ovs_checks,
236                 sudo = True)
237
238         msg = "Deleting the port %s" % self.get('port_name')
239         self.info(msg)
240
241         if proc.poll():
242             self.error(msg, out, err)
243             raise RuntimeError, msg
244
245         super(OVSPort, self).do_release()
246