applied the except and raise fixers to the master branch to close the gap with py3
[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 version 2 as
7 #    published by the Free Software Foundation;
8 #
9 #    This program is distributed in the hope that it will be useful,
10 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #    GNU General Public License for more details.
13 #
14 #    You should have received a copy of the GNU General Public License
15 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 #
17 # Authors: Alina Quereilhac <alina.quereilhac@inria.fr>
18 #         Alexandros Kouvakas <alexandros.kouvakas@inria.fr>
19 #         Julien Tribino <julien.tribino@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 @clsinit_copy                    
30 class PlanetlabOVSSwitch(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
39     """
40
41     _rtype = "planetlab::OVSSwitch"
42     _help = "Runs an OpenVSwitch on a PlanetLab host"
43     _platform = "planetlab"
44
45     _authorized_connections = ["planetlab::Node", "planetla::OVSPort", "linux::Node"]       
46
47     @classmethod
48     def _register_attributes(cls):
49         """ Register the attributes of OVSSwitch RM 
50
51         """
52         bridge_name = Attribute("bridge_name", 
53                 "Name of the switch/bridge",
54                 flags = Flags.Design)   
55         virtual_ip_pref = Attribute("virtual_ip_pref", 
56                 "Virtual IP/PREFIX of the switch",
57                 flags = Flags.Design)   
58         controller_ip = Attribute("controller_ip", 
59                 "IP of the controller",
60                 flags = Flags.Design)   
61         controller_port = Attribute("controller_port", 
62                 "Port of the controller",
63                 flags = Flags.Design)   
64
65         cls._register_attribute(bridge_name)
66         cls._register_attribute(virtual_ip_pref)
67         cls._register_attribute(controller_ip)
68         cls._register_attribute(controller_port)
69
70     def __init__(self, ec, guid):
71         """
72         :param ec: The Experiment controller
73         :type ec: ExperimentController
74         :param guid: guid of the RM
75         :type guid: int
76     
77         """
78         super(PlanetlabOVSSwitch, self).__init__(ec, guid)
79         self._home = "ovsswitch-%s" % self.guid
80         self._node = None
81
82     @property
83     def node(self):
84         """ Node wthat run the switch
85         """
86         if not self._node:
87             nodes = self.get_connected(PlanetlabNode.get_rtype())
88             if not nodes or len(nodes) != 1: 
89                 msg = "PlanetlabOVSSwitch must be connected to exactly one PlanetlabNode"
90                 #self.error(msg)
91                 raise RuntimeError(msg)
92
93             self._node = nodes[0]
94
95         return self._node
96
97     def valid_connection(self, guid):
98         """ Check if the connection with the guid in parameter is possible. Only meaningful connections are allowed.
99
100         :param guid: Guid of the current RM
101         :type guid: int
102         :rtype:  Boolean
103
104         """
105         rm = self.ec.get_resource(guid)
106         if rm.get_rtype() not in self._authorized_connections:
107             return False
108         return True
109
110     def do_provision(self):
111         self.node.mkdir(self.run_home)
112
113         self.check_sliver_ovs()
114         self.servers_on()
115         self.create_bridge()
116         self.assign_controller()
117         self.ovs_status()
118         
119         self.set_provisioned()
120                                 
121     def do_deploy(self):
122         """ Deploy the OVS Switch : Turn on the server, create the bridges
123             and assign the controller
124         """
125
126         if not self.node or self.node.state < ResourceState.READY:
127             self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.node.state)
128             self.ec.schedule(self.reschedule_delay, self.deploy)
129         else:
130             self.do_discover()
131             self.do_provision()
132                
133             self.set_ready()
134
135     def check_sliver_ovs(self):  
136         """ Check if sliver-ovs exists. If it does not exist, the execution is stopped
137         """
138         command = "compgen -c | grep sliver-ovs"                        
139         shfile = os.path.join(self.app_home, "check_ovs_cmd.sh")
140         try:
141             self.node.run_and_wait(command, self.run_home,
142                     shfile=shfile,
143                     sudo = True, 
144                     pidfile="check_ovs_cmd_pidfile",
145                     ecodefile="check_ovs_cmd_exitcode", 
146                     stdout="check_ovs_cmd_stdout", 
147                     stderr="check_ovs_cmd_stderr")
148         except RuntimeError:
149             msg = "Command sliver-ovs does not exist on the VM"          
150             self.debug(msg)
151             raise RuntimeError(msg)
152
153     def servers_on(self):
154         """ Start the openvswitch servers and check it
155         """
156         # Make sure the server is not running        
157         command = "sliver-ovs del-bridge %s; sliver-ovs stop" % self.get('bridge_name')
158         shfile = os.path.join(self.app_home, "clean.sh")
159         self.node.run_and_wait(command, self.run_home,
160                 shfile=shfile,
161                 sudo=True,
162                 raise_on_error=False,
163                 pidfile="clean_pidfile",
164                 ecodefile="clean_exitcode", 
165                 stdout="clean_stdout", 
166                 stderr="clean_stderr")
167
168         # start the server        
169         command = "sliver-ovs start"            
170         shfile = os.path.join(self.app_home, "start.sh")
171         try:
172             self.node.run_and_wait(command, self.run_home,
173                     shfile=shfile,
174                     sudo=True, 
175                     pidfile="start_pidfile",
176                     ecodefile="start_exitcode", 
177                     stdout="start_stdout", 
178                     stderr="start_stderr")
179         except RuntimeError:
180             msg = "Failed to start ovs-server on VM"             
181             self.debug(msg)
182             raise RuntimeError(msg)
183
184         command = "ps -A | grep ovsdb-server"
185         shfile = os.path.join(self.app_home, "ovsdb_status.sh")
186         try:
187             self.node.run_and_wait(command, self.run_home,
188                     shfile=shfile,
189                     sudo=True, 
190                     pidfile="ovsdb_status_pidfile",
191                     ecodefile="ovsdb_status_exitcode", 
192                     stdout="ovsdb_status_stdout", 
193                     stderr="ovsdb_status_stderr")
194         except RuntimeError:
195             msg = "ovsdb-server not running on VM"       
196             self.debug(msg)
197             raise RuntimeError(msg)
198         
199         self.info("Server OVS Started...")  
200
201     def create_bridge(self):
202         """ Create the bridge/switch and check error during SSH connection
203         """
204         # TODO: Check if previous bridge exist and delete them. Use ovs-vsctl list-br
205         # TODO: Add check for virtual_ip belonging to vsys_tag
206         if not (self.get("bridge_name") and self.get("virtual_ip_pref")):
207             msg = "No assignment in one or both attributes"
208             self.error(msg)
209             raise AttributeError(msg)
210
211         command = "sliver-ovs create-bridge '%s' '%s'" % (
212                           self.get("bridge_name"), 
213                           self.get("virtual_ip_pref")) 
214         
215         shfile = os.path.join(self.app_home, "bridge_create.sh")
216         try:
217             self.node.run_and_wait(command, self.run_home,
218                     shfile=shfile,
219                     sudo=True, 
220                     pidfile="bridge_create_pidfile",
221                     ecodefile="bridge_create_exitcode", 
222                     stdout="bridge_create_stdout", 
223                     stderr="bridge_create_stderr")
224         except RuntimeError:
225             msg = "No such pltap netdev\novs-appctl: ovs-vswitchd: server returned an error"
226             self.debug(msg)
227             raise RuntimeError(msg)
228
229         self.info(" Bridge %s Created and Assigned to %s" %\
230             (self.get("bridge_name"), self.get("virtual_ip_pref")) )
231
232     def assign_controller(self):
233         """ Set the controller IP
234         """
235
236         if not (self.get("controller_ip") and self.get("controller_port")):
237             return 
238
239         """
240         if not (self.get("controller_ip") and self.get("controller_port")):
241             msg = "No assignment in one or both attributes"
242             self.error(msg)
243             raise AttributeError, msg
244         """
245         command = "ovs-vsctl set-controller %s tcp:%s:%s" % \
246                 (self.get("bridge_name"), 
247                         self.get("controller_ip"), 
248                         self.get("controller_port"))
249         
250         shfile = os.path.join(self.app_home, "set_controller.sh")
251         try:
252             self.node.run_and_wait(command, self.run_home,
253                     shfile=shfile,
254                     sudo=True, 
255                     pidfile="set_controller_pidfile",
256                     ecodefile="set_controller_exitcode", 
257                     stdout="set_controller_stdout", 
258                     stderr="set_controller_stderr")
259         except RuntimeError:
260             msg = "SSH connection in the method assign_controller"
261             self.debug(msg)
262             raise RuntimeError(msg)
263
264         self.info("Controller assigned to the bridge %s" % self.get("bridge_name"))
265             
266     def ovs_status(self):
267         """ Print the status of the bridge                              
268         """
269         command = "sliver-ovs show | tail -n +2"
270         shfile = os.path.join(self.app_home, "ovs_status.sh")
271         try:
272             self.node.run_and_wait(command, self.run_home,
273                     shfile=shfile,
274                     sudo=True, 
275                     pidfile="ovs_status_pidfile",
276                     ecodefile="ovs_status_exitcode", 
277                     stdout="ovs_status_stdout", 
278                     stderr="ovs_status_stderr")
279         except RuntimeError:
280             msg = "Error when checking the status of the OpenVswitch"
281             self.debug(msg)
282             raise RuntimeError(msg)
283
284     def do_release(self):
285         """ Delete the bridge and close the server.  
286
287           .. note : It need to wait for the others RM (OVSPort and OVSTunnel)
288         to be released before releasing itself
289
290         """
291
292         from nepi.resources.planetlab.openvswitch.ovsport import PlanetlabOVSPort
293         rms = self.get_connected(PlanetlabOVSPort.get_rtype())
294
295         for rm in rms:
296             if rm.state < ResourceState.RELEASED:
297                 self.ec.schedule(self.reschedule_delay, self.release)
298                 return 
299             
300         msg = "Deleting the bridge %s" % self.get('bridge_name')
301         self.info(msg)
302         
303         command = "sliver-ovs del-bridge %s; sliver-ovs stop" % self.get('bridge_name')
304         shfile = os.path.join(self.app_home, "stop.sh")
305
306         self.node.run_and_wait(command, self.run_home,
307                 shfile=shfile,
308                 sudo=True, 
309                 pidfile="stop_pidfile",
310                 ecodefile="stop_exitcode", 
311                 stdout="stop_stdout", 
312                 stderr="stop_stderr")
313
314         super(PlanetlabOVSSwitch, self).do_release()
315