Modified FailureManager to abort only when critical resources fail
[nepi.git] / src / nepi / resources / planetlab / openvswitch / ovsport.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@gmail.com>
20
21 from nepi.execution.attribute import Attribute, Flags, Types
22 from nepi.execution.resource import ResourceManager, clsinit_copy, \
23         ResourceState, failtrap
24 from nepi.resources.planetlab.openvswitch.ovs import OVSWitch        
25 from nepi.resources.planetlab.node import PlanetlabNode        
26 from nepi.resources.linux.application import LinuxApplication
27
28 reschedule_delay = "0.5s"
29
30 @clsinit_copy                 
31 class OVSPort(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 = "OVSPort"
43     _help = "Runs an OpenVSwitch on a PlanetLab host"
44     _backend = "planetlab"
45
46     _authorized_connections = ["OVSWitch", "Tunnel"]      
47
48     @classmethod
49     def _register_attributes(cls):
50         """ Register the attributes of OVSPort RM 
51
52         """
53         port_name = Attribute("port_name", "Name of the port",
54             flags = Flags.ExecReadOnly)                 
55
56         cls._register_attribute(port_name)
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(OVSPort, self).__init__(ec, guid)
67         self._port_number = None
68         self.port_info = []          
69
70     @property
71     def node(self):
72         rm_list = self.get_connected(OVSWitch.rtype())
73         if rm_list:
74             for elt in rm_list:
75                 node = elt.get_connected(PlanetlabNode.rtype())
76                 if node: return node[0]
77         return node[0]
78
79     @property
80     def ovswitch(self):
81         ovswitch = self.get_connected(OVSWitch.rtype())
82         if ovswitch: return ovswitch[0]
83         return None
84         
85     @property
86     def port_number(self):
87         return self._port_number
88
89     def valid_connection(self, guid):
90         # TODO: Validate!
91         return True
92
93 #    def valid_connection(self, guid):
94 #        """ Check if the connection is available.
95
96 #        :param guid: Guid of the current RM
97 #        :type guid: int
98 #        :rtype:  Boolean
99
100 #        """
101 #        rm = self.ec.get_resource(guid)
102 #        if rm.rtype() in self._authorized_connections:
103 #            msg = "Connection between %s %s and %s %s accepted" % (self.rtype(), self._guid, rm.rtype(), guid)
104 #            self.debug(msg)
105 #            return True
106 #        msg = "Connection between %s %s and %s %s refused" % (self.rtype(), self._guid, rm.rtype(), guid)
107 #        self.debug(msg)
108
109     def get_host_ip(self):
110         """ Get the hostname of the node that
111         the port belongs to. We use it for tunnel.
112         """
113         get_host_ip = self.node
114         if not get_host_ip: 
115             msg = "info_list is empty"
116             self.debug(msg)
117             raise RuntimeError, msg
118         import socket
119         self.port_info.append(get_host_ip.get('hostname'))
120         self.port_info.append(socket.gethostbyname(self.port_info[0]))   
121     
122     def create_port(self):
123         """ Create the desired port
124         """
125         port_name = self.get('port_name')
126         if not (port_name or self.ovswitch):
127             msg = "The rm_list is empty or the port name is not assigned\n Failed to create port"
128             self.error(msg)
129             self.debug("ovswitch_list = %s and port_name = %s" % (self.ovswitch, port_name) )
130             raise AttributeError, msg
131
132         self.info("Create the port %s on switch %s" % (port_name, self.ovswitch.get('bridge_name')))     
133         self.port_info.append(port_name)
134         self.port_info.append(self.ovswitch.get('virtual_ip_pref'))
135         cmd = "sliver-ovs create-port %s %s" % (self.ovswitch.get('bridge_name'), port_name)   
136         self.node.run(cmd, self.ovswitch.ovs_checks, 
137                 stderr = "stdout-%s" % port_name, 
138                 stdout = "stderr-%s" % port_name,
139                 sudo = True)
140             
141     def get_local_end(self):
142         """ Get the local_endpoint of the port
143         """
144         msg = "Discovering the number of the port %s"\
145             % self.get('port_name')
146         self.info(msg)
147
148         command = "sliver-ovs get-local-endpoint %s"\
149             % self.get('port_name')
150         out = err = ""
151         (out, err), proc = self.node.run_and_wait(command, self.ovswitch.ovs_checks, 
152                 shfile = "port_number-%s.sh" % self.get('port_name'),
153                 pidfile = "port_number_pidfile-%s" % self.get('port_name'),
154                 ecodefile = "port_number_exitcode-%s" % self.get('port_name'), 
155                 sudo = True, 
156                 stdout = "stdout-%s" % self.get('port_name'),    
157                 stderr = "stderr-%s" % self.get('port_name'))
158
159         if err != "":
160             msg = "No assignment in attribute port_name"
161             self.error(msg)
162             self.debug("You are in the method get_local_end and the port_name = %s" % self.get('port_name'))
163             raise AttributeError, msg
164         self._port_number = None
165         self._port_number = int(out)
166         self.port_info.append(self._port_number)                                
167         self.info("The number of the %s is %s" % (self.get('port_name'), self._port_number))
168    
169     def switch_connect_command(self, local_port_name, 
170             remote_ip, remote_port_num):
171         """ Script for switch links
172         """
173         command = ["sliver-ovs"]
174         command.append("set-remote-endpoint ")
175         command.append("%s " % local_port_name)
176         command.append("%s " % remote_ip)
177         command.append("%s " % remote_port_num)
178         command = " ".join(command)
179         command = self.replace_paths(command)
180         return command
181         
182     @failtrap
183     def deploy(self):
184         """ Wait until ovswitch is started
185         """
186         ovswitch = self.ovswitch
187         if not ovswitch or ovswitch.state < ResourceState.READY:       
188             self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.ovswitch.state )  
189             self.ec.schedule(reschedule_delay, self.deploy)
190             
191         else:
192             self.discover()
193             self.provision()
194             self.get_host_ip()
195             self.create_port()
196             self.get_local_end()
197             self.ovswitch.ovs_status()
198             super(OVSPort, self).deploy()
199
200     def release(self):
201         """ Release the port RM means delete the ports
202         """
203         # OVS needs to wait until all associated RMs are released
204         # to be released
205         try:
206             from nepi.resources.planetlab.openvswitch.tunnel import Tunnel
207             rm = self.get_connected(Tunnel.rtype())
208             if rm and rm[0].state < ResourceState.FINISHED:
209                 self.ec.schedule(reschedule_delay, self.release)
210                 return 
211                 
212             msg = "Deleting the port %s" % self.get('port_name')
213             self.info(msg)
214             cmd = "sliver-ovs del_port %s" % self.get('port_name')
215             (out, err), proc = self.node.run(cmd, self.ovswitch.ovs_checks,
216                     sudo = True)
217
218             if proc.poll():
219                 self.fail()
220                 self.error(msg, out, err)
221                 raise RuntimeError, msg
222         except:
223             import traceback
224             err = traceback.format_exc()
225             self.error(err)
226
227         super(OVSPort, self).release()