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