c4873276cce7c8c06568739ce6df9290c096fdd8
[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
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"]
44
45     #alias2name = dict({'w0':'wlan0', 'w1':'wlan1'})
46
47     @classmethod
48     def _register_attributes(cls):
49         """Register the attributes of an OMF interface 
50
51         """
52         alias = Attribute("alias","Alias of the interface", default = "w0")
53         type = Attribute("type","Choose between : a, b, g, n")
54         name = Attribute("name","Alias of the interface", default = "wlan0")
55         mode = Attribute("mode","Mode of the interface")
56         hw_mode = Attribute("hw_mode","Choose between : a, b, g, n")
57         essid = Attribute("essid","Essid of the interface")
58         ip = Attribute("ip","IP of the interface")
59         cls._register_attribute(alias)
60         cls._register_attribute(type)
61         cls._register_attribute(name)
62         cls._register_attribute(mode)
63         cls._register_attribute(hw_mode)
64         cls._register_attribute(essid)
65         cls._register_attribute(ip)
66
67     def __init__(self, ec, guid):
68         """
69         :param ec: The Experiment controller
70         :type ec: ExperimentController
71         :param guid: guid of the RM
72         :type guid: int
73         :param creds: Credentials to communicate with the rm (XmppClient for OMF)
74         :type creds: dict
75
76         """
77         super(OMFWifiInterface, self).__init__(ec, guid)
78
79         self._conf = False
80
81         self._alias = self.get('alias')
82
83         self.create_id = None
84         self._create_cnt = 0
85         self.release_id = None
86         self._release_cnt = 0
87         self._topic_iface = None
88         self._omf_api = None
89         self._type = ""
90
91
92     def valid_connection(self, guid):
93         """ Check if the connection with the guid in parameter is possible. 
94         Only meaningful connections are allowed.
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.get_rtype() in self._authorized_connections:
103             msg = "Connection between %s %s and %s %s accepted" % \
104                 (self.get_rtype(), self._guid, rm.get_rtype(), guid)
105             self.debug(msg)
106             return True
107
108         msg = "Connection between %s %s and %s %s refused" % \
109              (self.get_rtype(), self._guid, rm.get_rtype(), guid)
110         self.debug(msg)
111         return False
112
113     @property
114     def exp_id(self):
115         return self.ec.exp_id
116
117     @property
118     def node(self):
119         rm_list = self.get_connected(OMFNode.get_rtype())
120         if rm_list: return rm_list[0]
121         return None
122
123     @property
124     def channel(self):
125         rm_list = self.get_connected(OMFChannel.get_rtype())
126         if rm_list: return rm_list[0]
127         return None
128
129     def configure_iface(self):
130         """ Configure the interface without the ip
131
132         """
133         if self.node.state < ResourceState.READY:
134             self.ec.schedule(reschedule_delay, self.deploy)
135             return False
136
137         for attrname in ["mode", "type", "essid"]:
138             attrval = self.get(attrname)
139             attrname = "net/%s/%s" % (self._alias, attrname)
140             self._omf_api.configure(self.node.get('hostname'), attrname, 
141                         attrval)
142         
143         super(OMFWifiInterface, self).do_provision()
144         return True
145
146     def configure_ip(self):
147         """ Configure the ip of the interface
148
149         """
150         if self.channel.state < ResourceState.READY:
151             self.ec.schedule(reschedule_delay, self.deploy)
152             return False
153
154         attrval = self.get("ip")
155         attrname = "net/%s/%s" % (self._alias, "ip")
156         self._omf_api.configure(self.node.get('hostname'), attrname, 
157                     attrval)
158         return True
159
160
161     def configure_on_omf5(self):
162         # Just for information
163 #        self.debug(" " + self.get_rtype() + " ( Guid : " + str(self._guid) +") : " + \
164 #            self.get('mode') + " : " + self.get('type') + " : " + \
165 #            self.get('essid') + " : " + self.get('ip'))
166         if self.state < ResourceState.PROVISIONED:
167             if self._conf == False:
168                 self._conf = self.configure_iface()
169                 res = self._conf
170         if self._conf == True:
171             res = self.configure_ip()
172         return res
173
174     def check_deploy(self, cid):
175         uid = self._omf_api.check_mailbox("create", cid)
176         if uid : 
177             return uid
178         return False
179
180     def do_deploy(self):
181         """ Deploy the RM. It means : Get the xmpp client and send messages 
182         using OMF 5.4 protocol to configure the interface.
183         It becomes DEPLOYED after sending messages to configure the interface
184         """
185         if not self.node or self.node.state < ResourceState.READY:
186             self.debug("---- RESCHEDULING DEPLOY ---- node state %s "
187                        % self.node.state )
188             self.ec.schedule(reschedule_delay, self.deploy)
189             return
190
191         if not self.channel or self.channel.state < ResourceState.READY:
192             self.debug("---- RESCHEDULING DEPLOY ---- channel state %s "
193                        % self.channel.state )
194             self.ec.schedule(reschedule_delay, self.deploy)
195             return
196
197         self.set('xmppUser',self.node.get('xmppUser'))
198         self.set('xmppServer',self.node.get('xmppServer'))
199         self.set('xmppPort',self.node.get('xmppPort'))
200         self.set('xmppPassword',self.node.get('xmppPassword'))
201         self.set('version',self.node.get('version'))
202
203         if not self.get('xmppServer'):
204             msg = "XmppServer is not initialzed. XMPP Connections impossible"
205             self.error(msg)
206             raise RuntimeError, msg
207
208         if not (self.get('xmppUser') or self.get('xmppPort') 
209                    or self.get('xmppPassword')):
210             msg = "Credentials are not all initialzed. Default values will be used"
211             self.warn(msg)
212
213         if not self._omf_api :
214             self._omf_api = OMFAPIFactory.get_api(self.get('version'), 
215               self.get('xmppServer'), self.get('xmppUser'), self.get('xmppPort'),
216                self.get('xmppPassword'), exp_id = self.exp_id)
217
218         if not (self.get('name') and self.get('mode') and self.get('essid') \
219                  and self.get('hw_mode') and self.get('ip')):
220             msg = "Interface's variable are not initialized"
221             self.error(msg)
222             raise RuntimeError, msg
223
224         self.set('type',self.get('hw_mode'))
225
226         if self.get('version') == "5":
227             res = self.configure_on_omf5()
228         else :
229             res = self.configure_on_omf6()
230
231         if res:
232             super(OMFWifiInterface, self).do_deploy()
233
234     def configure_on_omf6(self):
235         if not self.create_id :
236             props = {}
237             props['wlan:if_name'] = self.get('name')
238             props['wlan:mode'] = {
239                 "mode": self.get('mode'),
240                 "hw_mode" :  self.get('hw_mode'),
241                 "channel" : self.channel.get('channel'),
242                 "essid" : self.get('essid'),
243                 "ip_addr" : self.get('ip'),
244                 "frequency" : self.channel.frequency,
245                 "phy" : "%0%"
246                }
247             props['wlan:hrn'] = self.get('name')
248             props['wlan:type'] = "wlan"
249     
250             self.create_id = os.urandom(16).encode('hex')
251             self._omf_api.frcp_create( self.create_id, self.node.get('hostname'), "wlan", props = props)
252     
253         if self._create_cnt > confirmation_counter:
254             msg = "Couldn't retrieve the confirmation of the creation"
255             self.error(msg)
256             raise RuntimeError, msg
257
258         uid = self.check_deploy(self.create_id)
259         if not uid:
260             self._create_cnt +=1
261             self.ec.schedule(reschedule_delay, self.deploy)
262             return False
263
264         self._topic_iface = uid
265         self._omf_api.enroll_topic(self._topic_iface)
266         return True
267
268     def check_release(self, cid):
269         res = self._omf_api.check_mailbox("release", cid)
270         if res : 
271             return res
272         return False
273
274     def do_release(self):
275         """ Clean the RM at the end of the experiment and release the API
276
277         """
278         if self.get('version') == "6":
279             if not self.release_id:
280                 self.release_id = os.urandom(16).encode('hex')
281                 self._omf_api.frcp_release( self.release_id, self.node.get('hostname'),self._topic_iface, res_id=self._topic_iface)
282     
283             if self._release_cnt < confirmation_counter:
284                 cid = self.check_release(self.release_id)
285                 if not cid:
286                     self._release_cnt +=1
287                     self.ec.schedule(reschedule_delay, self.release)
288                     return
289             else:
290                 msg = "Couldn't retrieve the confirmation of the release"
291                 self.error(msg)
292
293         if self._omf_api:
294             OMFAPIFactory.release_api(self.get('version'), 
295               self.get('xmppServer'), self.get('xmppUser'), self.get('xmppPort'),
296                self.get('xmppPassword'), exp_id = self.exp_id)
297
298         super(OMFWifiInterface, self).do_release()
299