e01a7546a4ba45a0ca30afe9a7a5096eff33f584
[nepi.git] / src / nepi / resources / omf / omf5_api.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 ssl
22 import sys
23 import time
24
25 from nepi.util.logger import Logger
26
27 from nepi.resources.omf.omf_client import OMFClient
28 from nepi.resources.omf.messages_5_4 import MessageHandler
29
30 class OMF5API(Logger):
31     """
32     .. class:: Class Args :
33       
34         :param slice: Xmpp Slice
35         :type slice: str
36         :param host: Xmpp Server
37         :type host: str
38         :param port: Xmpp Port
39         :type port: str
40         :param password: Xmpp password
41         :type password: str
42         :param xmpp_root: Root of the Xmpp Topic Architecture
43         :type xmpp_root: str
44
45     .. note::
46
47        This class is the implementation of an OMF 5.4 API. 
48        Since the version 5.4.1, the Topic Architecture start with OMF_5.4 
49        instead of OMF used for OMF5.3
50
51     """
52     def __init__(self, slice, host, port, password, xmpp_root = None, 
53             exp_id = None):
54         """
55     
56         :param slice: Xmpp Slice
57         :type slice: str
58         :param host: Xmpp Server
59         :type host: str
60         :param port: Xmpp Port
61         :type port: str
62         :param password: Xmpp password
63         :type password: str
64         :param xmpp_root: Root of the Xmpp Topic Architecture
65         :type xmpp_root: str
66
67         """
68         super(OMFAPI, self).__init__("OMFAPI")
69         self._exp_id = exp_id 
70         self._user = "%s-%s" % (slice, self._exp_id)
71         self._slice = slice
72         self._host = host
73         self._port = port
74         self._password = password
75         self._hostnames = []
76         self._xmpp_root = xmpp_root or "OMF_5.4"
77
78         # OMF xmpp client
79         self._client = None
80
81         # message handler
82         self._message = None
83
84         if sys.version_info < (3, 0):
85             reload(sys)
86             sys.setdefaultencoding('utf8')
87
88         # instantiate the xmpp client
89         self._init_client()
90
91         # register xmpp nodes for the experiment
92         self._enroll_experiment()
93         self._enroll_newexperiment()
94
95         # register xmpp logger for the experiment
96         self._enroll_logger()
97
98     def _init_client(self):
99         """ Initialize XMPP Client
100
101         """
102         jid = "%s@%s" % (self._user, self._host)
103         xmpp = OMFClient(jid, self._password)
104         # PROTOCOL_SSLv3 required for compatibility with OpenFire
105         xmpp.ssl_version = ssl.PROTOCOL_SSLv3
106
107         if xmpp.connect((self._host, self._port)):
108             xmpp.process(block=False)
109             while not xmpp.ready:
110                 time.sleep(1)
111             self._client = xmpp
112             self._message = MessageHandler(self._slice, self._user)
113         else:
114             msg = "Unable to connect to the XMPP server."
115             self.error(msg)
116             raise RuntimeError(msg)
117
118     def _enroll_experiment(self):
119         """ Create and Subscribe to the Session Topic
120
121         """
122         xmpp_node = self._exp_session_id
123         self._client.create(xmpp_node)
124         #print "Create experiment sesion id topics !!" 
125         self._client.subscribe(xmpp_node)
126         #print "Subscribe to experiment sesion id topics !!" 
127
128
129     def _enroll_newexperiment(self):
130         """ Publish New Experiment Message
131
132         """
133         address = "/%s/%s/%s/%s" % (self._host, self._xmpp_root, self._slice,
134                 self._user)
135         #print address
136         payload = self._message.newexp_function(self._user, address)
137         slice_sid = "/%s/%s" % (self._xmpp_root, self._slice)
138         self._client.publish(payload, slice_sid)
139
140     def _enroll_logger(self):
141         """ Create and Subscribe to the Logger Topic
142
143         """
144         xmpp_node = self._logger_session_id
145         self._client.create(xmpp_node)
146         self._client.subscribe(xmpp_node)
147
148         payload = self._message.log_function("2", 
149                 "nodeHandler::NodeHandler", 
150                 "INFO", 
151                 "OMF Experiment Controller 5.4 (git 529a626)")
152         self._client.publish(payload, xmpp_node)
153
154     def _host_session_id(self, hostname):
155         """ Return the Topic Name as /xmpp_root/slice/user/hostname
156
157         :param hostname: Full hrn of the node
158         :type hostname: str
159
160         """
161         return "/%s/%s/%s/%s" % (self._xmpp_root, self._slice, self._user, 
162                 hostname)
163
164     def _host_resource_id(self, hostname):
165         """ Return the Topic Name as /xmpp_root/slice/resources/hostname
166
167         :param hostname: Full hrn of the node
168         :type hostname: str
169
170         """
171         return "/%s/%s/resources/%s" % (self._xmpp_root, self._slice, hostname)
172
173     @property
174     def _exp_session_id(self):
175         """ Return the Topic Name as /xmpp_root/slice/user
176
177         """
178         return "/%s/%s/%s" % (self._xmpp_root, self._slice, self._user)
179
180     @property
181     def _logger_session_id(self):
182         """ Return the Topic Name as /xmpp_root/slice/LOGGER
183
184         """
185         return "/%s/%s/%s/LOGGER" % (self._xmpp_root, self._slice, self._user)
186
187     def delete(self, hostname):
188         """ Delete the topic corresponding to the hostname for this session
189
190         :param hostname: Full hrn of the node
191         :type hostname: str
192
193         """
194         if not hostname in self._hostnames:
195             return
196
197         self._hostnames.remove(hostname)
198
199         xmpp_node = self._host_session_id(hostname)
200         self._client.delete(xmpp_node)
201
202     def enroll_host(self, hostname):
203         """ Create and Subscribe to the session topic and the resources
204             corresponding to the hostname
205
206         :param hostname: Full hrn of the node
207         :type hostname: str
208
209         """
210         if hostname in self._hostnames:
211             return 
212
213         self._hostnames.append(hostname)
214
215         xmpp_node =  self._host_session_id(hostname)
216         self._client.create(xmpp_node)
217         self._client.subscribe(xmpp_node)
218
219         xmpp_node =  self._host_resource_id(hostname)
220         self._client.subscribe(xmpp_node)
221
222         payload = self._message.enroll_function("1", "*", "1", hostname)
223         self._client.publish(payload, xmpp_node)
224
225     def configure(self, hostname, attribute, value):
226         """ Configure attribute on the node
227
228         :param hostname: Full hrn of the node
229         :type hostname: str
230         :param attribute: Attribute that need to be configured (
231             often written as /net/wX/attribute, with X the interface number)
232         :type attribute: str
233         :param value: Value of the attribute
234         :type value: str
235
236         """
237         payload = self._message.configure_function(hostname, value, attribute)
238         xmpp_node =  self._host_session_id(hostname)
239         self._client.publish(payload, xmpp_node)
240
241     
242     def send_stdin(self, hostname, value, app_id):
243         """ Send to the stdin of the application the value
244
245         :param hostname: Full hrn of the node
246         :type hostname: str
247         :param appid: Application Id (Any id that represents in a unique 
248             way the application)
249         :type appid: str
250         :param value: parameter to execute in the stdin of the application
251         :type value: str
252
253         """
254         payload = self._message.stdin_function(hostname, value, app_id)
255         xmpp_node =  self._host_session_id(hostname)
256         self._client.publish(payload, xmpp_node)
257
258
259     def execute(self, hostname, app_id, arguments, path, env):
260         """ Execute command on the node
261
262         :param hostname: Full hrn of the node
263         :type hostname: str
264         :param app_id: Application Id (Any id that represents in a unique 
265             way the application)
266         :type app_id: str
267         :param arguments: Arguments of the application
268         :type arguments: str
269         :param path: Path of the application
270         :type path: str
271         :param env: Environnement values for the application
272         :type env: str
273
274         """
275         payload = self._message.execute_function(hostname, app_id, arguments, 
276                 path, env)
277         xmpp_node =  self._host_session_id(hostname)
278         self._client.publish(payload, xmpp_node)
279
280     def exit(self, hostname, app_id):
281         """ Kill an application started with OMF
282
283         :param hostname: Full hrn of the node
284         :type hostname: str
285         :param app_id: Application Id of the application you want to stop
286         :type app_id: str
287
288         """
289         payload = self._message.exit_function(hostname, app_id)
290         xmpp_node =  self._host_session_id(hostname)
291         self._client.publish(payload, xmpp_node)
292
293     def release(self, hostname):
294         """ Delete the session and logger topics. Then disconnect 
295
296         """
297         if hostname in self._hostnames:
298             self.delete(hostname)
299
300     def disconnect(self) :
301         """ Delete the session and logger topics. Then disconnect 
302
303         """
304         self._client.delete(self._exp_session_id)
305         self._client.delete(self._logger_session_id)
306
307         time.sleep(1)
308         
309         # Wait the send queue to be empty before disconnect
310         self._client.disconnect(wait=True)
311         msg = " Disconnected from XMPP Server"
312         self.debug(msg)
313