7926fb60a655477180fe848b07f24f156b3dacde
[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
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.release_id = None
85         self._topic_iface = None
86         self._omf_api = None
87         self._type = ""
88
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(reschedule_delay, self.deploy)
133             return False
134
135         for attrname in ["mode", "type", "essid"]:
136             attrval = self.get(attrname)
137             attrname = "net/%s/%s" % (self._alias, attrname)
138             self._omf_api.configure(self.node.get('hostname'), attrname, 
139                         attrval)
140         
141         super(OMFWifiInterface, self).do_provision()
142         return True
143
144     def configure_ip(self):
145         """ Configure the ip of the interface
146
147         """
148         if self.channel.state < ResourceState.READY:
149             self.ec.schedule(reschedule_delay, self.deploy)
150             return False
151
152         attrval = self.get("ip")
153         attrname = "net/%s/%s" % (self._alias, "ip")
154         self._omf_api.configure(self.node.get('hostname'), attrname, 
155                     attrval)
156         return True
157
158
159     def configure_on_omf5(self):
160         # Just for information
161 #        self.debug(" " + self.get_rtype() + " ( Guid : " + str(self._guid) +") : " + \
162 #            self.get('mode') + " : " + self.get('type') + " : " + \
163 #            self.get('essid') + " : " + self.get('ip'))
164         if self.state < ResourceState.PROVISIONED:
165             if self._conf == False:
166                 self._conf = self.configure_iface()
167         if self._conf == True:
168             self.configure_ip()
169
170
171
172     def do_deploy(self):
173         """ Deploy the RM. It means : Get the xmpp client and send messages 
174         using OMF 5.4 protocol to configure the interface.
175         It becomes DEPLOYED after sending messages to configure the interface
176         """
177         if not self.node or self.node.state < ResourceState.READY:
178             self.debug("---- RESCHEDULING DEPLOY ---- node state %s "
179                        % self.node.state )
180             self.ec.schedule(reschedule_delay, self.deploy)
181             return
182
183         if not self.channel or self.channel.state < ResourceState.READY:
184             self.debug("---- RESCHEDULING DEPLOY ---- channel state %s "
185                        % self.channel.state )
186             self.ec.schedule(reschedule_delay, self.deploy)
187             return
188
189         self.set('xmppUser',self.node.get('xmppUser'))
190         self.set('xmppServer',self.node.get('xmppServer'))
191         self.set('xmppPort',self.node.get('xmppPort'))
192         self.set('xmppPassword',self.node.get('xmppPassword'))
193         self.set('version',self.node.get('version'))
194
195         if not self.get('xmppServer'):
196             msg = "XmppServer is not initialzed. XMPP Connections impossible"
197             self.error(msg)
198             raise RuntimeError, msg
199
200         if not (self.get('xmppUser') or self.get('xmppPort') 
201                    or self.get('xmppPassword')):
202             msg = "Credentials are not all initialzed. Default values will be used"
203             self.warn(msg)
204
205         if not self._omf_api :
206             self._omf_api = OMFAPIFactory.get_api(self.get('version'), 
207               self.get('xmppServer'), self.get('xmppUser'), self.get('xmppPort'),
208                self.get('xmppPassword'), exp_id = self.exp_id)
209
210         if not (self.get('name') and self.get('mode') and self.get('essid') \
211                  and self.get('hw_mode') and self.get('ip')):
212             msg = "Interface's variable are not initialized"
213             self.error(msg)
214             raise RuntimeError, msg
215
216         self.set('type',self.get('hw_mode'))
217
218         if self.get('version') == "5":
219             self.configure_on_omf5()
220         else :
221             self.configure_on_omf6()
222
223         super(OMFWifiInterface, self).do_deploy()
224
225     def configure_on_omf6(self):
226         if not self.create_id :
227             props = {}
228             props['wlan:if_name'] = self.get('name')
229             props['wlan:mode'] = {
230                 "mode": self.get('mode'),
231                 "hw_mode" :  self.get('hw_mode'),
232                 "channel" : self.channel.get('channel'),
233                 "essid" : self.get('essid'),
234                 "ip_addr" : self.get('ip'),
235                 "frequency" : self.channel.frequency,
236                 "phy" : "%0%"
237                }
238             props['wlan:hrn'] = self.get('name')
239             props['wlan:type'] = "wlan"
240     
241             self.create_id = os.urandom(16).encode('hex')
242             self._omf_api.frcp_create( self.create_id, self.node.get('hostname'), "wlan", props = props)
243     
244         self.check_deploy(self.create_id)
245         self._omf_api.enroll_topic(self._topic_iface)
246     
247     def check_deploy(self, cid):
248         delay = 1.0
249         for i in xrange(10):
250             uid = self._omf_api.check_mailbox("create", cid)
251             if uid:
252                 self._topic_iface = uid
253                 break
254             else:
255                 time.sleep(delay)
256                 delay = delay * 1.5
257         else:
258             msg = "Couldn't retrieve the confirmation of the creation"
259             self.error(msg)
260             raise RuntimeError, msg
261
262     def check_release(self, cid):
263         res = self._omf_api.check_mailbox("release", cid)
264         if res : 
265             return res
266         return False
267
268     def do_release(self):
269         """ Clean the RM at the end of the experiment and release the API
270
271         """
272         if self.get('version') == "6":
273             if not self.release_id:
274                 self.release_id = os.urandom(16).encode('hex')
275                 self._omf_api.frcp_release( self.release_id, self.node.get('hostname'),self._topic_iface, res_id=self._topic_iface)
276     
277             cid = self.check_release(self.release_id)
278             if not cid:
279                 self.ec.schedule(reschedule_delay, self.release)
280                 return
281
282         if self._omf_api:
283             OMFAPIFactory.release_api(self.get('version'), 
284               self.get('xmppServer'), self.get('xmppUser'), self.get('xmppPort'),
285                self.get('xmppPassword'), exp_id = self.exp_id)
286
287         super(OMFWifiInterface, self).do_release()
288