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