Changes for release
[nepi.git] / src / nepi / resources / omf / interface.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 #         Julien Tribino <julien.tribino@inria.fr>
20
21 import os, time
22 from nepi.execution.resource import ResourceManager, clsinit_copy, \
23         ResourceState, reschedule_delay
24 from nepi.execution.attribute import Attribute, Flags 
25
26 from nepi.resources.omf.node import OMFNode, confirmation_counter, reschedule_check
27 from nepi.resources.omf.omf_resource import ResourceGateway, OMFResource
28 from nepi.resources.omf.channel import OMFChannel
29 from nepi.resources.omf.omf_api_factory import OMFAPIFactory
30
31 @clsinit_copy
32 class OMFWifiInterface(OMFResource):
33     """
34     .. class:: Class Args :
35       
36         :param ec: The Experiment controller
37         :type ec: ExperimentController
38         :param guid: guid of the RM
39         :type guid: int
40
41     """
42     _rtype = "OMFWifiInterface"
43     _authorized_connections = ["OMFNode" , "OMFChannel", "WilabtSfaNode"]
44
45     @classmethod
46     def _register_attributes(cls):
47         """Register the attributes of an OMF interface 
48
49         """
50         name = Attribute("name","Alias of the interface : wlan0, wlan1, ..", default = "wlan0")
51         mode = Attribute("mode","Mode of the interface")
52         hw_mode = Attribute("hw_mode","Choose between : a, b, g, n")
53         essid = Attribute("essid","Essid of the interface")
54         ip = Attribute("ip","IP of the interface")
55         cls._register_attribute(name)
56         cls._register_attribute(mode)
57         cls._register_attribute(hw_mode)
58         cls._register_attribute(essid)
59         cls._register_attribute(ip)
60
61     def __init__(self, ec, guid):
62         """
63         :param ec: The Experiment controller
64         :type ec: ExperimentController
65         :param guid: guid of the RM
66         :type guid: int
67         :param creds: Credentials to communicate with the rm (XmppClient for OMF)
68         :type creds: dict
69
70         """
71         super(OMFWifiInterface, self).__init__(ec, guid)
72
73         self._conf = False
74         self.alias = None
75         self._type = None
76
77         self.create_id = None
78         self._create_cnt = 0
79         self.release_id = None
80         self._release_cnt = 0
81         self._topic_iface = None
82         self._omf_api = None
83         self._type = ""
84
85
86     def valid_connection(self, guid):
87         """ Check if the connection with the guid in parameter is possible. 
88         Only meaningful connections are allowed.
89
90         :param guid: Guid of the current RM
91         :type guid: int
92         :rtype:  Boolean
93
94         """
95         rm = self.ec.get_resource(guid)
96         if rm.get_rtype() in self._authorized_connections:
97             msg = "Connection between %s %s and %s %s accepted" % \
98                 (self.get_rtype(), self._guid, rm.get_rtype(), guid)
99             self.debug(msg)
100             return True
101
102         msg = "Connection between %s %s and %s %s refused" % \
103              (self.get_rtype(), self._guid, rm.get_rtype(), guid)
104         self.debug(msg)
105         return False
106
107     @property
108     def exp_id(self):
109         return self.ec.exp_id
110
111     @property
112     def node(self):
113         rm_list = self.get_connected(OMFNode.get_rtype())
114         if rm_list: return rm_list[0]
115         return None
116
117     @property
118     def channel(self):
119         rm_list = self.get_connected(OMFChannel.get_rtype())
120         if rm_list: return rm_list[0]
121         return None
122
123     def configure_iface(self):
124         """ Configure the interface without the ip
125
126         """
127         if self.node.state < ResourceState.READY:
128             self.ec.schedule(reschedule_delay, self.deploy)
129             return False
130
131         for attrname in ["mode", "type", "essid"]:
132             if attrname == "type" :
133                 attrval = self._type
134             else :
135                 attrval = self.get(attrname)
136             attrname = "net/%s/%s" % (self.alias, attrname)
137             self._omf_api.configure(self.node.get('hostname'), attrname, 
138                         attrval)
139         
140         super(OMFWifiInterface, self).do_provision()
141         return True
142
143     def configure_ip(self):
144         """ Configure the ip of the interface
145
146         .. note : The ip is separated from the others parameters to avoid 
147         CELL ID shraing problem. By putting th ip at the end of the configuration, 
148         each node use the same channel and can then share the same CELL ID.
149         In the second case, the channel is defined at the end and the node don't
150         share a common CELL ID and can not communicate.
151
152         """
153         if self.channel.state < ResourceState.READY:
154             self.ec.schedule(reschedule_delay, self.deploy)
155             return False
156
157         attrval = self.get("ip")
158         if '/' in attrval:
159            attrval,mask = attrval.split('/')
160         attrname = "net/%s/%s" % (self.alias, "ip")
161         self._omf_api.configure(self.node.get('hostname'), attrname, 
162                     attrval)
163         return True
164
165
166     def configure_on_omf5(self):
167         """ Method to configure the wifi interface when OMF 5.4 is used.
168
169         """    
170
171         self._type = self.get('hw_mode')
172         if self.get('name') == "wlan0" or "eth0":
173             self.alias = "w0"
174         else:    
175             self.alias = "w1"
176         res = False
177         if self.state < ResourceState.PROVISIONED:
178             if self._conf == False:
179                 self._conf = self.configure_iface()
180         if self._conf == True:
181             res = self.configure_ip()
182         return res
183
184     def check_deploy(self, cid):
185         """ Check, through the mail box in the parser, 
186         if the confirmation of the creation has been received
187
188         :param cid: the id of the original message
189         :type guid: string
190
191         """
192         uid = self._omf_api.check_mailbox("create", cid)
193         if uid : 
194             return uid
195         return False
196
197     def do_deploy(self):
198         """ Deploy the RM. It means : Get the xmpp client and send messages 
199         using OMF 5.4 or 6 protocol to configure the interface.
200
201         """
202         if not self.node or self.node.state < ResourceState.READY:
203             self.debug("---- RESCHEDULING DEPLOY ---- node state %s "
204                        % self.node.state )
205             self.ec.schedule(reschedule_delay, self.deploy)
206             return
207
208         if not self.channel or self.channel.state < ResourceState.READY:
209             self.debug("---- RESCHEDULING DEPLOY ---- channel state %s "
210                        % self.channel.state )
211             self.ec.schedule(reschedule_delay, self.deploy)
212             return
213
214         self.set('xmppUser',self.node.get('xmppUser'))
215         self.set('xmppServer',self.node.get('xmppServer'))
216         self.set('xmppPort',self.node.get('xmppPort'))
217         self.set('xmppPassword',self.node.get('xmppPassword'))
218         self.set('version',self.node.get('version'))
219
220         if not self.get('xmppServer'):
221             msg = "XmppServer is not initialzed. XMPP Connections impossible"
222             self.error(msg)
223             raise RuntimeError, msg
224
225         if not (self.get('xmppUser') or self.get('xmppPort') 
226                    or self.get('xmppPassword')):
227             msg = "Credentials are not all initialzed. Default values will be used"
228             self.warn(msg)
229
230         if not self._omf_api :
231             self._omf_api = OMFAPIFactory.get_api(self.get('version'), 
232               self.get('xmppServer'), self.get('xmppUser'), self.get('xmppPort'),
233                self.get('xmppPassword'), exp_id = self.exp_id)
234
235         if not (self.get('name')):
236             msg = "Interface's name is not initialized"
237             self.error(msg)
238             raise RuntimeError, msg
239
240         if not (self.get('mode') and self.get('essid') \
241                  and self.get('hw_mode') and self.get('ip')):
242             msg = "Interface's variable are not initialized"
243             self.error(msg)
244             raise RuntimeError, msg
245
246         if self.get('version') == "5":
247             res = self.configure_on_omf5()        
248
249         else :
250             res = self.configure_on_omf6()
251
252         if res:
253             super(OMFWifiInterface, self).do_deploy()
254
255     def configure_on_omf6(self):
256         """ Method to configure the wifi interface when OMF 6 is used.
257
258         """   
259         if not self.create_id :
260             props = {}
261             props['wlan:if_name'] = self.get('name')
262             props['wlan:mode'] = {
263                 "mode": self.get('mode'),
264                 "hw_mode" :  self.get('hw_mode'),
265                 "channel" : self.channel.get('channel'),
266                 "essid" : self.get('essid'),
267                 "ip_addr" : self.get('ip'),
268                 "frequency" : self.channel.frequency,
269                 "phy" : "%0%"
270                }
271             props['wlan:hrn'] = self.get('name')
272             props['wlan:type'] = "wlan"
273     
274             self.create_id = os.urandom(16).encode('hex')
275             self._omf_api.frcp_create( self.create_id, self.node.get('hostname'), "wlan", props = props)
276     
277         if self._create_cnt > confirmation_counter:
278             msg = "Couldn't retrieve the confirmation of the creation"
279             self.error(msg)
280             raise RuntimeError, msg
281
282         uid = self.check_deploy(self.create_id)
283         if not uid:
284             self._create_cnt +=1
285             self.ec.schedule(reschedule_check, self.deploy)
286             return False
287
288         self._topic_iface = uid
289         self._omf_api.enroll_topic(self._topic_iface)
290         return True
291
292     def check_release(self, cid):
293         """ Check, through the mail box in the parser, 
294         if the confirmation of the release has been received
295
296         :param cid: the id of the original message
297         :type guid: string
298
299         """
300         res = self._omf_api.check_mailbox("release", cid)
301         if res : 
302             return res
303         return False
304
305     def do_release(self):
306         """ Clean the RM at the end of the experiment and release the API
307
308         """
309         if self._omf_api:
310             if self.get('version') == "6":
311                 if not self.release_id:
312                     self.release_id = os.urandom(16).encode('hex')
313                     self._omf_api.frcp_release( self.release_id, self.node.get('hostname'),self._topic_iface, res_id=self._topic_iface)
314     
315                 if self._release_cnt < confirmation_counter:
316                     cid = self.check_release(self.release_id)
317                     if not cid:
318                         self._release_cnt +=1
319                         self.ec.schedule(reschedule_check, self.release)
320                         return
321                 else:
322                     msg = "Couldn't retrieve the confirmation of the release"
323                     self.error(msg)
324
325
326             OMFAPIFactory.release_api(self.get('version'), 
327               self.get('xmppServer'), self.get('xmppUser'), self.get('xmppPort'),
328                self.get('xmppPassword'), exp_id = self.exp_id)
329
330         super(OMFWifiInterface, self).do_release()
331