Bug fixing and ordering openvswitch code
[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 PlanetlabOVSSwitch(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", 
54                 "Name of the switch/bridge",
55                 flags = Flags.Design)   
56         virtual_ip_pref = Attribute("virtual_ip_pref", 
57                 "Virtual IP/PREFIX of the switch",
58                 flags = Flags.Design)   
59         controller_ip = Attribute("controller_ip", 
60                 "IP of the controller",
61                 flags = Flags.Design)   
62         controller_port = Attribute("controller_port", 
63                 "Port of the controller",
64                 flags = Flags.Design)   
65
66         cls._register_attribute(bridge_name)
67         cls._register_attribute(virtual_ip_pref)
68         cls._register_attribute(controller_ip)
69         cls._register_attribute(controller_port)
70
71     def __init__(self, ec, guid):
72         """
73         :param ec: The Experiment controller
74         :type ec: ExperimentController
75         :param guid: guid of the RM
76         :type guid: int
77     
78         """
79         super(PlanetlabOVSSwitch, self).__init__(ec, guid)
80         self._home = "ovsswitch-%s" % self.guid
81         self._node = None
82
83     @property
84     def node(self):
85         """ Node wthat run the switch
86         """
87         if not self._node:
88             nodes = self.get_connected(PlanetlabNode.get_rtype())
89             if not nodes or len(nodes) != 1: 
90                 msg = "PlanetlabOVSSwitch must be connected to exactly one PlanetlabNode"
91                 #self.error(msg)
92                 raise RuntimeError, msg
93
94             self._node = nodes[0]
95
96         return self._node
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() not in self._authorized_connections:
108             return False
109         return True
110
111     def do_provision(self):
112         self.node.mkdir(self.run_home)
113
114         self.check_sliver_ovs()
115         self.servers_on()
116         self.create_bridge()
117         self.assign_controller()
118         self.ovs_status()
119         
120         self.set_provisioned()
121                                 
122     def do_deploy(self):
123         """ Deploy the OVS Switch : Turn on the server, create the bridges
124             and assign the controller
125         """
126
127         if not self.node or self.node.state < ResourceState.READY:
128             self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.node.state)
129             self.ec.schedule(self.reschedule_delay, self.deploy)
130         else:
131             self.do_discover()
132             self.do_provision()
133                
134             self.set_ready()
135
136     def check_sliver_ovs(self):  
137         """ Check if sliver-ovs exists. If it does not exist, the execution is stopped
138         """
139         command = "compgen -c | grep sliver-ovs"                        
140         shfile = os.path.join(self.app_home, "check_ovs_cmd.sh")
141         try:
142             self.node.run_and_wait(command, self.run_home,
143                     shfile=shfile,
144                     sudo = True, 
145                     pidfile="check_ovs_cmd_pidfile",
146                     ecodefile="check_ovs_cmd_exitcode", 
147                     stdout="check_ovs_cmd_stdout", 
148                     stderr="check_ovs_cmd_stderr")
149         except RuntimeError:
150             msg = "Command sliver-ovs does not exist on the VM"          
151             self.debug(msg)
152             raise RuntimeError, msg
153
154     def servers_on(self):
155         """ Start the openvswitch servers and check it
156         """
157         # Make sure the server is not running        
158         command = "sliver-ovs del-bridge %s; sliver-ovs stop" % self.get('bridge_name')
159         shfile = os.path.join(self.app_home, "clean.sh")
160         self.node.run_and_wait(command, self.run_home,
161                 shfile=shfile,
162                 sudo=True,
163                 raise_on_error=False,
164                 pidfile="clean_pidfile",
165                 ecodefile="clean_exitcode", 
166                 stdout="clean_stdout", 
167                 stderr="clean_stderr")
168
169         # start the server        
170         command = "sliver-ovs start"            
171         shfile = os.path.join(self.app_home, "start.sh")
172         try:
173             self.node.run_and_wait(command, self.run_home,
174                     shfile=shfile,
175                     sudo=True, 
176                     pidfile="start_pidfile",
177                     ecodefile="start_exitcode", 
178                     stdout="start_stdout", 
179                     stderr="start_stderr")
180         except RuntimeError:
181             msg = "Failed to start ovs-server on VM"             
182             self.debug(msg)
183             raise RuntimeError, msg
184
185         command = "ps -A | grep ovsdb-server"
186         shfile = os.path.join(self.app_home, "ovsdb_status.sh")
187         try:
188             self.node.run_and_wait(command, self.run_home,
189                     shfile=shfile,
190                     sudo=True, 
191                     pidfile="ovsdb_status_pidfile",
192                     ecodefile="ovsdb_status_exitcode", 
193                     stdout="ovsdb_status_stdout", 
194                     stderr="ovsdb_status_stderr")
195         except RuntimeError:
196             msg = "ovsdb-server not running on VM"       
197             self.debug(msg)
198             raise RuntimeError, msg
199         
200         self.info("Server OVS Started...")  
201
202     def create_bridge(self):
203         """ Create the bridge/switch and check error during SSH connection
204         """
205         # TODO: Check if previous bridge exist and delete them. Use ovs-vsctl list-br
206         # TODO: Add check for virtual_ip belonging to vsys_tag
207         if not (self.get("bridge_name") and self.get("virtual_ip_pref")):
208             msg = "No assignment in one or both attributes"
209             self.error(msg)
210             raise AttributeError, msg
211
212         command = "sliver-ovs create-bridge '%s' '%s'" % (
213                           self.get("bridge_name"), 
214                           self.get("virtual_ip_pref")) 
215         
216         shfile = os.path.join(self.app_home, "bridge_create.sh")
217         try:
218             self.node.run_and_wait(command, self.run_home,
219                     shfile=shfile,
220                     sudo=True, 
221                     pidfile="bridge_create_pidfile",
222                     ecodefile="bridge_create_exitcode", 
223                     stdout="bridge_create_stdout", 
224                     stderr="bridge_create_stderr")
225         except RuntimeError:
226             msg = "No such pltap netdev\novs-appctl: ovs-vswitchd: server returned an error"
227             self.debug(msg)
228             raise RuntimeError, msg
229
230         self.info(" Bridge %s Created and Assigned to %s" %\
231             (self.get("bridge_name"), self.get("virtual_ip_pref")) )
232
233     def assign_controller(self):
234         """ Set the controller IP
235         """
236
237         if not (self.get("controller_ip") and self.get("controller_port")):
238             return 
239
240         """
241         if not (self.get("controller_ip") and self.get("controller_port")):
242             msg = "No assignment in one or both attributes"
243             self.error(msg)
244             raise AttributeError, msg
245         """
246         command = "ovs-vsctl set-controller %s tcp:%s:%s" % \
247                 (self.get("bridge_name"), 
248                         self.get("controller_ip"), 
249                         self.get("controller_port"))
250         
251         shfile = os.path.join(self.app_home, "set_controller.sh")
252         try:
253             self.node.run_and_wait(command, self.run_home,
254                     shfile=shfile,
255                     sudo=True, 
256                     pidfile="set_controller_pidfile",
257                     ecodefile="set_controller_exitcode", 
258                     stdout="set_controller_stdout", 
259                     stderr="set_controller_stderr")
260         except RuntimeError:
261             msg = "SSH connection in the method assign_controller"
262             self.debug(msg)
263             raise RuntimeError, msg
264
265         self.info("Controller assigned to the bridge %s" % self.get("bridge_name"))
266             
267     def ovs_status(self):
268         """ Print the status of the bridge                              
269         """
270         command = "sliver-ovs show | tail -n +2"
271         shfile = os.path.join(self.app_home, "ovs_status.sh")
272         try:
273             self.node.run_and_wait(command, self.run_home,
274                     shfile=shfile,
275                     sudo=True, 
276                     pidfile="ovs_status_pidfile",
277                     ecodefile="ovs_status_exitcode", 
278                     stdout="ovs_status_stdout", 
279                     stderr="ovs_status_stderr")
280         except RuntimeError:
281             msg = "Error when checking the status of the OpenVswitch"
282             self.debug(msg)
283             raise RuntimeError, msg
284
285     def do_release(self):
286         """ Delete the bridge and close the server.  
287
288           .. note : It need to wait for the others RM (OVSPort and OVSTunnel)
289         to be released before releasing itself
290
291         """
292
293         from nepi.resources.planetlab.openvswitch.ovsport import PlanetlabOVSPort
294         rms = self.get_connected(PlanetlabOVSPort.get_rtype())
295
296         for rm in rms:
297             if rm.state < ResourceState.RELEASED:
298                 self.ec.schedule(self.reschedule_delay, self.release)
299                 return 
300             
301         msg = "Deleting the bridge %s" % self.get('bridge_name')
302         self.info(msg)
303         
304         command = "sliver-ovs del-bridge %s; sliver-ovs stop" % self.get('bridge_name')
305         shfile = os.path.join(self.app_home, "stop.sh")
306
307         self.node.run_and_wait(command, self.run_home,
308                 shfile=shfile,
309                 sudo=True, 
310                 pidfile="stop_pidfile",
311                 ecodefile="stop_exitcode", 
312                 stdout="stop_stdout", 
313                 stderr="stop_stderr")
314
315         super(PlanetlabOVSSwitch, self).do_release()
316