Push openflow RMs
[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, ResourceState       
23 from nepi.resources.planetlab.openvswitch.ovs import OVSWitch        
24 from nepi.resources.planetlab.node import PlanetlabNode        
25 from nepi.resources.linux.application import LinuxApplication
26
27 reschedule_delay = "0.5s"
28
29 @clsinit_copy                 
30 class OVSPort(LinuxApplication):
31     """
32     .. class:: Class Args :
33       
34         :param ec: The Experiment controller
35         :type ec: ExperimentController
36         :param guid: guid of the RM
37         :type guid: int
38         :param creds: Credentials to communicate with the rm 
39         :type creds: dict
40
41     """
42     
43     _rtype = "OVSPort"
44     _authorized_connections = ["OVSWitch", "Tunnel"]      
45
46     @classmethod
47     def _register_attributes(cls):
48         """ Register the attributes of OVSPort RM 
49
50         """
51         port_name = Attribute("port_name", "Name of the port",
52             flags = Flags.ExecReadOnly)                 
53
54         cls._register_attribute(port_name)
55
56     def __init__(self, ec, guid):
57         """
58         :param ec: The Experiment controller
59         :type ec: ExperimentController
60         :param guid: guid of the RM
61         :type guid: int
62     
63         """
64         super(OVSPort, self).__init__(ec, guid)
65         self._port_number = None
66         self.port_info = []          
67
68     @property
69     def node(self):
70         rm_list = self.get_connected(OVSWitch.rtype())
71         if rm_list:
72             for elt in rm_list:
73                 node = elt.get_connected(PlanetlabNode.rtype())
74                 if node: return node[0]
75         return node[0]
76
77     @property
78     def ovswitch(self):
79         ovswitch = self.get_connected(OVSWitch.rtype())
80         if ovswitch: return ovswitch[0]
81         return None
82         
83     @property
84     def port_number(self):
85         return self._port_number
86
87     def valid_connection(self, guid):
88         # TODO: Validate!
89         return True
90
91 #    def valid_connection(self, guid):
92 #        """ Check if the connection is available.
93
94 #        :param guid: Guid of the current RM
95 #        :type guid: int
96 #        :rtype:  Boolean
97
98 #        """
99 #        rm = self.ec.get_resource(guid)
100 #        if rm.rtype() in self._authorized_connections:
101 #            msg = "Connection between %s %s and %s %s accepted" % (self.rtype(), self._guid, rm.rtype(), guid)
102 #            self.debug(msg)
103 #            return True
104 #        msg = "Connection between %s %s and %s %s refused" % (self.rtype(), self._guid, rm.rtype(), guid)
105 #        self.debug(msg)
106
107     def get_host_ip(self):
108         """ Get the hostname of the node that
109         the port belongs to. We use it for tunnel.
110         """
111         get_host_ip = self.node
112         if not get_host_ip: 
113             msg = "info_list is empty"
114             self.debug(msg)
115             raise RuntimeError, msg
116         import socket
117         self.port_info.append(get_host_ip.get('hostname'))
118         self.port_info.append(socket.gethostbyname(self.port_info[0]))   
119     
120     def create_port(self):
121         """ Create the desired port
122         """
123         port_name = self.get('port_name')
124         if not (port_name or self.ovswitch):
125             msg = "The rm_list is empty or the port name is not assigned\n Failed to create port"
126             self.error(msg)
127             self.debug("ovswitch_list = %s and port_name = %s" % (self.ovswitch, port_name) )
128             raise AttributeError, msg
129
130         self.info("Create the port %s on switch %s" % (port_name, self.ovswitch.get('bridge_name')))     
131         self.port_info.append(port_name)
132         self.port_info.append(self.ovswitch.get('virtual_ip_pref'))
133         cmd = "sliver-ovs create-port %s %s" % (self.ovswitch.get('bridge_name'), port_name)   
134         self.node.run(cmd, self.ovswitch.ovs_checks, 
135                 stderr = "stdout-%s" % port_name, 
136                 stdout = "stderr-%s" % port_name,
137                 sudo = True)
138             
139     def get_local_end(self):
140         """ Get the local_endpoint of the port
141         """
142         msg = "Discovering the number of the port %s"\
143             % self.get('port_name')
144         self.info(msg)
145
146         command = "sliver-ovs get-local-endpoint %s"\
147             % self.get('port_name')
148         out = err = ""
149         (out, err), proc = self.node.run_and_wait(command, self.ovswitch.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 = "No assignment in attribute port_name"
159             self.error(msg)
160             self.debug("You are in the method get_local_end and the port_name = %s" % self.get('port_name'))
161             raise AttributeError, msg
162         self._port_number = None
163         self._port_number = int(out)
164         self.port_info.append(self._port_number)                                
165         self.info("The number of the %s is %s" % (self.get('port_name'), self._port_number))
166    
167     def switch_connect_command(self, local_port_name, 
168             remote_ip, remote_port_num):
169         """ Script for switch links
170         """
171         command = ["sliver-ovs"]
172         command.append("set-remote-endpoint ")
173         command.append("%s " % local_port_name)
174         command.append("%s " % remote_ip)
175         command.append("%s " % remote_port_num)
176         command = " ".join(command)
177         command = self.replace_paths(command)
178         return command
179         
180     def provision(self):
181         """ Provision the ports.No meaning.
182         """
183         pass
184
185     def discover(self):
186         """ Discover the ports.No meaning
187         """     
188         pass
189
190     def deploy(self):
191         """ Wait until ovswitch is started
192         """
193         ovswitch = self.ovswitch
194         if not ovswitch or ovswitch.state < ResourceState.READY:       
195             self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.ovswitch.state )  
196             self.ec.schedule(reschedule_delay, self.deploy)
197             
198         else:
199             try:
200                 self.discover()
201                 self.provision()
202                 self.get_host_ip()
203                 self.create_port()
204                 self.get_local_end()
205                 self.ovswitch.ovs_status()
206                 self._state = ResourceState.READY
207             except:
208                 self._state = ResourceState.FAILED
209                 raise
210
211     def start(self):
212         """ Start the RM. It means nothing special for 
213             ovsport for now.
214         """
215         pass
216         
217     def stop(self):
218         """ Stop the RM. It means nothing special for 
219             ovsport for now.        
220         """
221         pass
222         
223     def release(self):
224         """ Release the port RM means delete the ports
225         """
226         # OVS needs to wait until all associated RMs are released
227         # to be released
228         from nepi.resources.planetlab.openvswitch.tunnel import Tunnel
229         rm = self.get_connected(Tunnel.rtype())
230         if rm[0].state < ResourceState.FINISHED:
231             self.ec.schedule(reschedule_delay, self.release)
232             return 
233             
234         msg = "Deleting the port %s" % self.get('port_name')
235         self.info(msg)
236         cmd = "sliver-ovs del_port %s" % self.get('port_name')
237         (out, err), proc = self.node.run(cmd, self.ovswitch.ovs_checks,
238                 sudo = True)
239
240         if proc.poll():
241             self.fail()
242             self.error(msg, out, err)
243             raise RuntimeError, msg
244
245         self._state = ResourceState.RELEASED