last modifications about Openflow after merging
[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 # Authors: Alina Quereilhac <alina.quereilhac@inria.fr>
19 #         Alexandros Kouvakas <alexandros.kouvakas@inria.fr>
20 #         Julien Tribino <julien.tribino@inria.fr>
21
22 from nepi.execution.attribute import Attribute, Flags, Types
23 from nepi.execution.resource import ResourceManager, clsinit_copy, \
24         ResourceState
25 from nepi.resources.planetlab.openvswitch.ovs import OVSSwitch        
26 from nepi.resources.planetlab.node import PlanetlabNode        
27 from nepi.resources.linux.application import LinuxApplication
28
29 reschedule_delay = "0.5s"
30
31 @clsinit_copy                 
32 class OVSPort(LinuxApplication):
33     """
34     .. class:: Class Args :
35       
36         :param ec: The Experiment controller
37         :type ec: ExperimentController
38         :param guid: guid of the RM
39         :type guid: int
40
41     """
42     
43     _rtype = "OVSPort"
44     _help = "Runs an OpenVSwitch on a PlanetLab host"
45     _backend = "planetlab"
46
47     _authorized_connections = ["OVSSwitch", "OVSTunnel"]      
48
49     @classmethod
50     def _register_attributes(cls):
51         """ Register the attributes of OVSPort RM 
52
53         """
54         port_name = Attribute("port_name", "Name of the port",
55             flags = Flags.Design)                       
56
57         cls._register_attribute(port_name)
58
59     def __init__(self, ec, guid):
60         """
61         :param ec: The Experiment controller
62         :type ec: ExperimentController
63         :param guid: guid of the RM
64         :type guid: int
65     
66         """
67         super(OVSPort, self).__init__(ec, guid)
68         self._port_number = None
69         self.port_info = []          
70
71     def log_message(self, msg):
72         return " guid %d - OVSPort - %s " % (self.guid, msg)
73
74     @property
75     def node(self):
76         """ Node that run the switch and the ports
77         """
78         rm_list = self.get_connected(OVSSwitch.get_rtype())
79         if rm_list:
80             for elt in rm_list:
81                 node = elt.get_connected(PlanetlabNode.get_rtype())
82                 if node: return node[0]
83         return node[0]
84
85     @property
86     def ovsswitch(self):
87         """ Switch where the port is created
88         """
89         ovsswitch = self.get_connected(OVSSwitch.get_rtype())
90         if ovsswitch: return ovsswitch[0]
91         return None
92         
93     @property
94     def port_number(self):
95         return self._port_number
96
97     def valid_connection(self, guid):
98         """ Check if the connection is available.
99
100         :param guid: Guid of the current RM
101         :type guid: int
102         :rtype:  Boolean
103
104         """
105         rm = self.ec.get_resource(guid)
106         if rm.get_rtype() in self._authorized_connections:
107             msg = "Connection between %s %s and %s %s accepted" % (self.get_rtype(), self._guid, rm.get_rtype(), guid)
108             self.debug(msg)
109             return True
110         msg = "Connection between %s %s and %s %s refused" % (self.get_rtype(), self._guid, rm.get_rtype(), guid)
111         self.debug(msg)
112
113     def create_port(self):
114         """ Create the desired port
115         """
116         msg = "Creating the port %s" % self.get('port_name')
117         self.debug(msg)
118
119         if not self.get('port_name'):
120             msg = "The port name is not assigned"
121             self.error(msg)
122             raise AttributeError, msg
123
124         if not self.ovsswitch:
125             msg = "The OVSwitch RM is not running"
126             self.error(msg)
127             raise AttributeError, msg
128
129         cmd = "sliver-ovs create-port %s %s" % (self.ovsswitch.get('bridge_name'),
130                                                 self.get('port_name'))   
131         self.node.run(cmd, self.ovsswitch.ovs_checks, 
132                 stderr = "stdout-%s" % self.get('port_name'), 
133                 stdout = "stderr-%s" % self.get('port_name'),
134                 sudo = True)
135
136         self.info("Created the port %s on switch %s" % (self.get('port_name'),
137                                              self.ovsswitch.get('bridge_name')))     
138             
139     def get_local_end(self):
140         """ Get the local_endpoint of the port
141         """
142
143         msg = "Discovering the number of the port %s" % self.get('port_name')
144         self.debug(msg)
145
146         command = "sliver-ovs get-local-endpoint %s" % self.get('port_name')
147         out = err = ""
148         (out, err), proc = self.node.run_and_wait(command, 
149                 self.ovsswitch.ovs_checks,
150                 shfile = "port_number-%s.sh" % self.get('port_name'),
151                 pidfile = "port_number_pidfile-%s" % self.get('port_name'),
152                 ecodefile = "port_number_exitcode-%s" % self.get('port_name'), 
153                 sudo = True, 
154                 stdout = "stdout-%s" % self.get('port_name'),    
155                 stderr = "stderr-%s" % self.get('port_name'))
156
157         if err != "":
158             msg = "Error retrieving the local endpoint of the port"
159             self.error(msg)
160             raise AttributeError, msg
161
162         if out:
163             self._port_number = int(out)
164
165         self.info("The number of the %s is %s" % (self.get('port_name'), 
166            self.port_number))
167    
168     def set_port_info(self):
169         """ Set all the information about the port inside a list
170         """
171
172         info = []
173         info.append(self.node.get('hostname'))
174
175         #Return the ip of the node
176         import socket
177         ip = socket.gethostbyname(self.node.get('hostname'))
178         info.append(ip)
179
180         info.append(self.get('port_name'))
181         info.append(self.ovsswitch.get('virtual_ip_pref'))
182         info.append(self.port_number)
183         return info
184
185     def switch_connect_command(self, local_port_name, 
186             remote_ip, remote_port_num):
187         """ Script to create the connection from a switch to a 
188              remote endpoint
189         """
190
191         command = ["sliver-ovs"]
192         command.append("set-remote-endpoint ")
193         command.append("%s " % local_port_name)
194         command.append("%s " % remote_ip)
195         command.append("%s " % remote_port_num)
196         command = " ".join(command)
197         command = self.replace_paths(command)
198         return command
199         
200     def do_deploy(self):
201         """ Deploy the OVS port after the OVS Switch
202         """
203
204         if not self.ovsswitch or self.ovsswitch.state < ResourceState.READY:       
205             self.debug("---- RESCHEDULING DEPLOY ---- OVSwitch state %s " % self.ovsswitch.state )  
206             self.ec.schedule(reschedule_delay, self.deploy)
207             return
208
209         self.do_discover()
210         self.do_provision()
211
212         self.create_port()
213         self.get_local_end()
214
215         #Check the status of the OVS Switch
216         self.ovsswitch.ovs_status()
217
218         # Save all the information inside a list
219         self.port_info = self.set_port_info()
220
221         super(OVSPort, self).do_deploy()
222
223     def do_release(self):
224         """ Delete the port on the OVSwitch. It needs to wait for the tunnel
225         to be released.
226         """
227
228         from nepi.resources.planetlab.openvswitch.tunnel import OVSTunnel
229         rm = self.get_connected(OVSTunnel.get_rtype())
230
231         if rm and rm[0].state < ResourceState.RELEASED:
232             self.ec.schedule(reschedule_delay, self.release)
233             return 
234             
235         cmd = "sliver-ovs del_port %s" % self.get('port_name')
236         (out, err), proc = self.node.run(cmd, self.ovsswitch.ovs_checks,
237                 sudo = True)
238
239         msg = "Deleting the port %s" % self.get('port_name')
240         self.info(msg)
241
242         if proc.poll():
243             self.error(msg, out, err)
244             raise RuntimeError, msg
245
246         super(OVSPort, self).do_release()
247