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