fb921e97783ff9808732c284b7e017378b23fb56
[nepi.git] / src / nepi / resources / planetlab / openvswitch / ovs.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
23 from nepi.execution.resource import ResourceManager, clsinit_copy, \
24         ResourceState
25 from nepi.execution.attribute import Attribute, Flags
26 from nepi.resources.planetlab.node import PlanetlabNode        
27 from nepi.resources.linux.application import LinuxApplication
28 import os
29
30 reschedule_delay = "0.5s"
31
32 @clsinit_copy                    
33 class OVSSwitch(LinuxApplication):
34     """
35     .. class:: Class Args :
36       
37         :param ec: The Experiment controller
38         :type ec: ExperimentController
39         :param guid: guid of the RM
40         :type guid: int
41
42     """
43
44     _rtype = "OVSSwitch"
45     _help = "Runs an OpenVSwitch on a PlanetLab host"
46     _backend = "planetlab"
47
48     _authorized_connections = ["PlanetlabNode", "OVSPort", "LinuxNode"]       
49
50     @classmethod
51     def _register_attributes(cls):
52         """ Register the attributes of OVSSwitch RM 
53
54         """
55         bridge_name = Attribute("bridge_name", "Name of the switch/bridge",
56                 flags = Flags.Design)   
57         virtual_ip_pref = Attribute("virtual_ip_pref", "Virtual IP/PREFIX of the switch",
58                 flags = Flags.Design)   
59         controller_ip = Attribute("controller_ip", "IP of the controller",
60                 flags = Flags.Design)   
61         controller_port = Attribute("controller_port", "Port of the controller",
62                 flags = Flags.Design)   
63
64         cls._register_attribute(bridge_name)
65         cls._register_attribute(virtual_ip_pref)
66         cls._register_attribute(controller_ip)
67         cls._register_attribute(controller_port)
68
69     def __init__(self, ec, guid):
70         """
71         :param ec: The Experiment controller
72         :type ec: ExperimentController
73         :param guid: guid of the RM
74         :type guid: int
75     
76         """
77         super(OVSSwitch, self).__init__(ec, guid)
78         self._home = "ovsswitch-%s" % self.guid
79         self._checks = "ovsChecks-%s" % self.guid
80
81     @property
82     def node(self):
83         """ Node wthat run the switch
84         """
85         node = self.get_connected(PlanetlabNode.get_rtype())
86         if node: return node[0]
87         return None
88
89     def log_message(self, msg):
90         return " guid %d - OVSSwitch - %s " % (self.guid, msg)
91
92     @property
93     def ovs_home(self):
94         return os.path.join(self.node.exp_home, self._home)
95
96     @property
97     def ovs_checks(self):
98         return os.path.join(self.ovs_home, self._checks)
99
100     def valid_connection(self, guid):
101         """ Check if the connection with the guid in parameter is possible. Only meaningful connections are allowed.
102
103         :param guid: Guid of the current RM
104         :type guid: int
105         :rtype:  Boolean
106
107         """
108         rm = self.ec.get_resource(guid)
109         if rm.get_rtype() in self._authorized_connections:
110             msg = "Connection between %s %s and %s %s accepted" % \
111                 (self.get_rtype(), self._guid, rm.get_rtype(), guid)
112             self.debug(msg)
113             return True
114         msg = "Connection between %s %s and %s %s refused" % \
115              (self.get_rtype(), self._guid, rm.get_rtype(), guid)
116         self.debug(msg)
117         return False
118
119     def do_provision(self):
120         """ Create the different OVS folder.
121         """
122
123         # create home dir for ovs
124         self.node.mkdir(self.ovs_home)
125         # create dir for ovs checks
126         self.node.mkdir(self.ovs_checks)
127         
128         super(OVSSwitch, self).do_provision()
129                                 
130     def do_deploy(self):
131         """ Deploy the OVS Switch : Turn on the server, create the bridges
132             and assign the controller
133         """
134
135         if not self.node or self.node.state < ResourceState.READY:
136             self.ec.schedule(reschedule_delay, self.deploy)
137             return
138
139         self.do_discover()
140         self.do_provision()
141
142         self.check_sliver_ovs()
143         self.servers_on()
144         self.create_bridge()
145         self.assign_controller()
146         self.ovs_status()
147             
148         super(OVSSwitch, self).do_deploy()
149
150     def check_sliver_ovs(self):  
151         """ Check if sliver-ovs exists. If it does not exist, the execution is stopped
152         """
153
154         cmd = "compgen -c | grep sliver-ovs"                    
155         out = err = ""
156
157         (out,err), proc = self.node.run_and_wait(cmd, self.ovs_checks, 
158                     shfile = "check_cmd.sh",
159                 pidfile = "check_cmd_pidfile",
160                 ecodefile = "check_cmd_exitcode", 
161                 sudo = True, 
162                 stdout = "check_cmd_stdout", 
163                 stderr = "check_cmd_stderr")
164
165         (out, err), proc = self.node.check_output(self.ovs_checks, 'check_cmd_exitcode')
166         
167         if out != "0\n":
168             msg = "Command sliver-ovs does not exist on the VM"          
169             self.debug(msg)
170             raise RuntimeError, msg
171
172         msg = "Command sliver-ovs exists" 
173         self.debug(msg)
174
175     def servers_on(self):
176         """ Start the openvswitch servers and check it
177         """
178
179         # Start the server
180         command = "sliver-ovs start"            
181         out = err = ""                                                                  
182         (out, err), proc = self.node.run_and_wait(command, self.ovs_checks,   
183                 shfile = "start_srv.sh",
184                 pidfile = "start_srv_pidfile",
185                 ecodefile = "start_srv_exitcode", 
186                 sudo = True, 
187                 raise_on_error = True,
188                 stdout = "start_srv_stdout", 
189                 stderr = "start_srv_stderr")
190         (out, err), proc = self.node.check_output(self.ovs_checks, 'start_srv_exitcode')
191
192         if out != "0\n":
193             self.error("Servers have not started")
194             raise RuntimeError, msg     
195                                 
196         # Check if the servers are running or not
197         cmd = "ps -A | grep ovsdb-server"
198         out = err = ""
199         (out, err), proc = self.node.run_and_wait(cmd, self.ovs_checks, 
200                 shfile = "status_srv.sh",
201                 pidfile = "status_srv_pidfile",
202                 ecodefile = "status_srv_exitcode", 
203                 sudo = True, 
204                 stdout = "status_srv_stdout", 
205                 stderr = "status_srv_stderr")
206         (out, err), proc = self.node.check_output(self.ovs_checks, 'status_srv_exitcode')
207         
208         if out != "0\n":
209             msg = "Servers are not running"
210             self.error(msg)
211             raise RuntimeError, msg
212         
213         self.info("Server OVS Started Correctly")  
214
215     def create_bridge(self):
216         """ Create the bridge/switch and check error during SSH connection
217         """
218         # TODO: Check if previous bridge exist and delete them. Use ovs-vsctl list-br
219         # TODO: Add check for virtual_ip belonging to vsys_tag
220
221         
222         if not (self.get("bridge_name") and self.get("virtual_ip_pref")):
223             msg = "No assignment in one or both attributes"
224             self.error(msg)
225             raise AttributeError, msg
226
227         cmd = "sliver-ovs create-bridge '%s' '%s'" %\
228             (self.get("bridge_name"), self.get("virtual_ip_pref")) 
229         out = err = ""
230         (out, err), proc = self.node.run_and_wait(cmd, self.ovs_checks,
231                 shfile = "create_br.sh",
232                 pidfile = "create_br_pidfile",
233                 ecodefile = "create_br_exitcode", 
234                 sudo = True, 
235                 stdout = "create_br_stdout", 
236                 stderr = "create_br_stderr") 
237         (out, err), proc = self.node.check_output(self.ovs_checks, 'create_br_exitcode')
238
239         if out != "0\n":
240             msg = "No such pltap netdev\novs-appctl: ovs-vswitchd: server returned an error"
241             self.error(msg)                     
242             raise RuntimeError, msg
243
244         self.info(" Bridge %s Created and Assigned to %s" %\
245             (self.get("bridge_name"), self.get("virtual_ip_pref")) )
246           
247
248     def assign_controller(self):
249         """ Set the controller IP
250         """
251
252         if not (self.get("controller_ip") and self.get("controller_port")):
253             msg = "No assignment in one or both attributes"
254             self.error(msg)
255             raise AttributeError, msg
256
257         cmd = "ovs-vsctl set-controller %s tcp:%s:%s" %\
258             (self.get("bridge_name"), self.get("controller_ip"), self.get("controller_port"))
259         out = err = ""
260         (out, err), proc = self.node.run(cmd, self.ovs_checks,
261                 sudo = True, 
262                 stdout = "stdout", 
263                 stderr = "stderr")
264
265         if err != "":
266             msg = "SSH connection in the method assign_controller"
267             self.error(msg)
268             raise RuntimeError, msg
269
270         self.info("Controller assigned to the bridge %s" % self.get("bridge_name"))
271             
272     def ovs_status(self):
273         """ Print the status of the bridge                              
274         """
275
276         cmd = "sliver-ovs show | tail -n +2"
277         out = err = ""
278         (out, err), proc = self.node.run_and_wait(cmd, self.ovs_home,
279                 sudo = True, 
280                 stdout = "show_stdout", 
281                 stderr = "show_stderr") 
282         (out, err), proc = self.node.check_output(self.ovs_home, 'show_stdout')
283         
284         if out == "":
285             msg = "Error when checking the status of the OpenVswitch"
286             self.error(msg)
287             raise RuntimeError, msg
288         
289         self.debug(out)
290
291     def do_release(self):
292         """ Delete the bridge and close the server.  
293
294           .. note : It need to wait for the others RM (OVSPort and OVSTunnel)
295         to be released before releasing itself
296
297         """
298
299         from nepi.resources.planetlab.openvswitch.ovsport import OVSPort
300         rms = self.get_connected(OVSPort.get_rtype())
301
302         for rm in rms :
303             if rm.state < ResourceState.RELEASED:
304                 self.ec.schedule(reschedule_delay, self.release)
305                 return 
306             
307         cmd = "sliver-ovs del-bridge %s" % self.get('bridge_name')
308         (out, err), proc = self.node.run(cmd, self.ovs_checks,
309                 sudo = True)
310
311         cmd = "sliver-ovs stop"
312         (out, err), proc = self.node.run(cmd, self.ovs_checks,
313                 sudo = True)
314
315         msg = "Deleting the bridge %s" % self.get('bridge_name')
316         self.info(msg)
317         
318         if proc.poll():
319             self.error(msg, out, err)
320             raise RuntimeError, msg
321
322         super(OVSSwitch, self).do_release()
323