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