Fixing RM.DEPLOY being executed after/during RM.RELEASE by adding a release_lock...
[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 from nepi.execution.resource import ResourceManager, clsinit_copy, \
22         ResourceState, reschedule_delay
23 from nepi.execution.attribute import Attribute, Flags 
24
25 from nepi.resources.omf.node import OMFNode
26 from nepi.resources.omf.omf_resource import ResourceGateway, OMFResource
27 from nepi.resources.omf.channel import OMFChannel
28 from nepi.resources.omf.omf_api import OMFAPIFactory
29
30 @clsinit_copy
31 class OMFWifiInterface(OMFResource):
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         :param creds: Credentials to communicate with the rm (XmppClient for OMF)
40         :type creds: dict
41
42     .. note::
43
44        This class is used only by the Experiment Controller through the Resource 
45        Factory
46
47     """
48     _rtype = "OMFWifiInterface"
49     _authorized_connections = ["OMFNode" , "OMFChannel"]
50
51     #alias2name = dict({'w0':'wlan0', 'w1':'wlan1'})
52
53     @classmethod
54     def _register_attributes(cls):
55         """Register the attributes of an OMF interface 
56
57         """
58         alias = Attribute("alias","Alias of the interface", default = "w0")
59         mode = Attribute("mode","Mode of the interface")
60         type = Attribute("type","Type of the interface")
61         essid = Attribute("essid","Essid of the interface")
62         ip = Attribute("ip","IP of the interface")
63         cls._register_attribute(alias)
64         cls._register_attribute(mode)
65         cls._register_attribute(type)
66         cls._register_attribute(essid)
67         cls._register_attribute(ip)
68
69     def __init__(self, ec, guid):
70         """
71         :param ec: The Experiment controller
72         :type ec: ExperimentController
73         :param guid: guid of the RM
74         :type guid: int
75         :param creds: Credentials to communicate with the rm (XmppClient for OMF)
76         :type creds: dict
77
78         """
79         super(OMFWifiInterface, self).__init__(ec, guid)
80
81         self._omf_api = None
82         self._alias = self.get('alias')
83
84     def valid_connection(self, guid):
85         """ Check if the connection with the guid in parameter is possible. 
86         Only meaningful connections are allowed.
87
88         :param guid: Guid of the current RM
89         :type guid: int
90         :rtype:  Boolean
91
92         """
93         rm = self.ec.get_resource(guid)
94         if rm.rtype() in self._authorized_connections:
95             msg = "Connection between %s %s and %s %s accepted" % \
96                 (self.rtype(), self._guid, rm.rtype(), guid)
97             self.debug(msg)
98
99             return True
100
101         msg = "Connection between %s %s and %s %s refused" % \
102              (self.rtype(), self._guid, rm.rtype(), guid)
103         self.debug(msg)
104
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.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.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         try :
132             for attrname in ["mode", "type", "essid"]:
133                 attrval = self.get(attrname)
134                 attrname = "net/%s/%s" % (self._alias, attrname)
135                 self._omf_api.configure(self.node.get('hostname'), attrname, 
136                         attrval)
137         except AttributeError:
138             self._state = ResourceState.FAILED
139             msg = "Credentials are not initialzed. XMPP Connections impossible"
140             self.debug(msg)
141             #raise
142         
143         super(OMFWifiInterface, self).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         try :
155             attrval = self.get("ip")
156             attrname = "net/%s/%s" % (self._alias, "ip")
157             self._omf_api.configure(self.node.get('hostname'), attrname, 
158                     attrval)
159         except AttributeError:
160             msg = "Credentials are not initialzed. XMPP Connections impossible"
161             self.debug(msg)
162             self.fail()
163             #raise
164
165         return True
166
167     def do_deploy(self):
168         """ Deploy the RM. It means : Get the xmpp client and send messages 
169         using OMF 5.4 protocol to configure the interface.
170         It becomes DEPLOYED after sending messages to configure the interface
171         """
172         if not self._omf_api :
173             self._omf_api = OMFAPIFactory.get_api(self.get('xmppSlice'), 
174                 self.get('xmppHost'), self.get('xmppPort'), 
175                 self.get('xmppPassword'), exp_id = self.exp_id)
176
177         if not self._omf_api :
178             msg = "Credentials are not initialzed. XMPP Connections impossible"
179             self.error(msg)
180             raise RuntimeError, msg
181
182         if not (self.get('mode') and self.get('type') and self.get('essid') \
183                 and self.get('ip')):
184             msg = "Interface's variable are not initialized"
185             self.error(msg)
186             raise RuntimeError, msg
187
188         if not self.node.get('hostname') :
189             msg = "The channel is connected with an undefined node"
190             self.error(msg)
191             raise RuntimeError, msg
192
193         # Just for information
194         self.debug(" " + self.rtype() + " ( Guid : " + str(self._guid) +") : " + \
195             self.get('mode') + " : " + self.get('type') + " : " + \
196             self.get('essid') + " : " + self.get('ip'))
197     
198         # Check if the node is already deployed
199         if self.state < ResourceState.PROVISIONED:
200             if self.configure_iface():
201                 self.configure_ip()
202
203         super(OMFWifiInterface, self).do_deploy()
204
205     def do_release(self):
206         """ Clean the RM at the end of the experiment and release the API
207
208         """
209         if self._omf_api:
210             OMFAPIFactory.release_api(self.get('xmppSlice'), 
211                 self.get('xmppHost'), self.get('xmppPort'), 
212                 self.get('xmppPassword'), exp_id = self.exp_id)
213
214         super(OMFWifiInterface, self).do_release()
215