update test and examples for OMF6 in OMF section
[nepi.git] / src / nepi / resources / omf / omf_client.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.util.logger import Logger
22 from nepi.resources.omf.omf6_parser import OMF6Parser
23
24 try:
25     import sleekxmpp
26     from sleekxmpp.exceptions import IqError, IqTimeout
27     class BaseOMFClient(sleekxmpp.ClientXMPP):
28         pass
29 except ImportError:
30     print "SleekXMPP is not installed. Without this library, \n" + \
31           " You will be not able to use OMF Resources \n"+ \
32           " If you want to install SleekXmpp : \n"+ \
33           " git clone -b develop git://github.com/fritzy/SleekXMPP.git \n"+ \
34           " cd SleekXMPP \n"+ \
35            "sudo python setup.py install\n"
36     class BaseOMFClient(object):
37         pass
38
39 import traceback
40 import xml.etree.ElementTree as ET
41
42 # inherit from BaseXmpp and XMLstream classes
43 class OMFClient(sleekxmpp.ClientXMPP, Logger): 
44     """
45     .. class:: Class Args :
46       
47         :param jid: Jabber Id (= Xmpp Slice + Date)
48         :type jid: str
49         :param password: Jabber Password (= Xmpp Password)
50         :type password: str
51
52     .. note::
53
54        This class is an XMPP Client with customized method
55
56     """
57
58     def __init__(self, jid, password):
59         """
60
61         :param jid: Jabber Id (= Xmpp Slice + Date)
62         :type jid: str
63         :param password: Jabber Password (= Xmpp Password)
64         :type password: str
65
66
67         """
68         Logger.__init__(self, "OMFClient")
69
70         sleekxmpp.ClientXMPP.__init__(self, jid, password)
71         self._ready = False
72         self._registered = False
73         self._server = None
74         self._parser = None
75
76         self.register_plugin('xep_0077') # In-band registration
77         self.register_plugin('xep_0030')
78         self.register_plugin('xep_0059')
79         self.register_plugin('xep_0060') # PubSub 
80
81         self.add_event_handler("session_start", self.start)
82         self.add_event_handler("register", self.register)
83         self.add_event_handler("pubsub_publish", self.handle_omf_message)
84
85         #Init the parser
86         self._init_parser()
87         
88     def _init_parser(self):
89         """ Init the parser depending on the OMF Version
90
91         """
92         self._parser = OMF6Parser()
93
94     @property
95     def ready(self):
96         """ Check if the client is ready
97
98         """
99         return self._ready
100
101     def start(self, event):
102         """ Send presence to the Xmppp Server. This function is called directly by the sleekXmpp library
103
104         """
105         self.send_presence()
106         self._ready = True
107         self._server = "pubsub.%s" % self.boundjid.domain
108
109     def register(self, iq):
110         """  Register to the Xmppp Server. This function is called directly by the sleekXmpp library
111
112         """
113         if self._registered:
114             msg = " %s already registered!" % self.boundjid
115             self.info(msg)
116             return 
117
118         resp = self.Iq()
119         resp['type'] = 'set'
120         resp['register']['username'] = self.boundjid.user
121         resp['register']['password'] = self.password
122
123         try:
124             resp.send(now=True)
125             msg = " Account created for %s!" % self.boundjid
126             self.info(msg)
127             self._registered = True
128         except IqError as e:
129             msg = " Could not register account: %s" % e.iq['error']['text']
130             self.error(msg)
131         except IqTimeout:
132             msg = " No response from server."
133             self.error(msg)
134
135     def unregister(self):
136         """  Unregister from the Xmppp Server.
137
138         """
139         try:
140             self.plugin['xep_0077'].cancel_registration(
141                 ifrom=self.boundjid.full)
142             msg = " Account unregistered for %s!" % self.boundjid
143             self.info(msg)
144         except IqError as e:
145             msg = " Could not unregister account: %s" % e.iq['error']['text']
146             self.error(msg)
147         except IqTimeout:
148             msg = " No response from server."
149             self.error(msg)
150
151     def nodes(self):
152         """  Get all the nodes of the Xmppp Server.
153
154         """
155         try:
156             result = self['xep_0060'].get_nodes(self._server)
157             for item in result['disco_items']['items']:
158                 msg = ' - %s' % str(item)
159                 self.debug(msg)
160             return result
161         except:
162             error = traceback.format_exc()
163             msg = 'Could not retrieve node list.\ntraceback:\n%s' % error
164             self.error(msg)
165
166     def subscriptions(self):
167         """  Get all the subscriptions of the Xmppp Server.
168
169         """
170         try:
171             result = self['xep_0060'].get_subscriptions(self._server)
172                 #self.boundjid.full)
173             for node in result['node']:
174                 msg = ' - %s' % str(node)
175                 self.debug(msg)
176             return result
177         except:
178             error = traceback.format_exc()
179             msg = ' Could not retrieve subscriptions.\ntraceback:\n%s' % error
180             self.error(msg)
181
182     def create(self, node):
183         """  Create the topic corresponding to the node
184
185         :param node: Name of the topic, corresponding to the node (ex : omf.plexus.wlab17)
186         :type node: str
187
188         """
189         msg = " Create Topic : " + node
190         self.info(msg)
191    
192         config = self['xep_0004'].makeForm('submit')
193         config.add_field(var='pubsub#node_type', value='leaf')
194         config.add_field(var='pubsub#notify_retract', value='0')
195         config.add_field(var='pubsub#publish_model', value='open')
196         config.add_field(var='pubsub#persist_items', value='1')
197         config.add_field(var='pubsub#max_items', value='1')
198         config.add_field(var='pubsub#title', value=node)
199
200         try:
201             self['xep_0060'].create_node(self._server, node, config = config)
202         except:
203             #error = traceback.format_exc()
204             #msg = ' Could not create topic: %s\ntraceback:\n%s' % (node, error)
205             msg = 'Could not create the topic : '+node+' . Maybe the topic already exists'
206             self.error(msg)
207
208     def delete(self, node):
209         """  Delete the topic corresponding to the node
210
211         :param node: Name of the topic, corresponding to the node (ex : omf.plexus.wlab17)
212         :type node: str
213
214         """
215         # To check if the queue are well empty at the end
216         #print " length of the queue : " + str(self.send_queue.qsize())
217         #print " length of the queue : " + str(self.event_queue.qsize())
218         try:
219             self['xep_0060'].delete_node(self._server, node)
220             msg = ' Deleted node: %s' % node
221             self.info(msg)
222         except:
223             #error = traceback.format_exc()
224             #msg = ' Could not delete topic: %s\ntraceback:\n%s' % (node, error)
225             msg = 'Could not delete the topic : '+node+' . Maybe It is not the owner of the topic'
226             self.error(msg)
227     
228     def publish(self, data, node):
229         """  Publish the data to the corresponding topic
230
231         :param data: Data that will be published
232         :type data: str
233         :param node: Name of the topic
234         :type node: str
235
236         """ 
237
238         msg = " Publish to Topic : " + node
239         self.info(msg)
240         try:
241             result = self['xep_0060'].publish(self._server,node,payload=data)
242             # id = result['pubsub']['publish']['item']['id']
243             # print('Published at item id: %s' % id)
244         except:
245             error = traceback.format_exc()
246             msg = ' Could not publish to: %s\ntraceback:\n%s' % (node, error)
247             self.error(msg)
248
249     def get(self, data):
250         """  Get the item
251
252         :param data: data from which the items will be get back
253         :type data: str
254
255
256         """
257         try:
258             result = self['xep_0060'].get_item(self._server, self.boundjid,
259                 data)
260             for item in result['pubsub']['items']['substanzas']:
261                 msg = 'Retrieved item %s: %s' % (item['id'], tostring(item['payload']))
262                 self.debug(msg)
263         except:
264             error = traceback.format_exc()
265             msg = ' Could not retrieve item %s from topic %s\ntraceback:\n%s' \
266                     % (data, self.boundjid, error)
267             self.error(msg)
268
269     def retract(self, data):
270         """  Retract the item
271
272         :param data: data from which the item will be retracted
273         :type data: str
274
275         """
276         try:
277             result = self['xep_0060'].retract(self._server, self.boundjid, data)
278             msg = ' Retracted item %s from topic %s' % (data, self.boundjid)
279             self.debug(msg)
280         except:
281             error = traceback.format_exc()
282             msg = 'Could not retract item %s from topic %s\ntraceback:\n%s' \
283                     % (data, self.boundjid, error)
284             self.error(msg)
285
286     def purge(self):
287         """  Purge the information in the server
288
289         """
290         try:
291             result = self['xep_0060'].purge(self._server, self.boundjid)
292             msg = ' Purged all items from topic %s' % self.boundjid
293             self.debug(msg)
294         except:
295             error = traceback.format_exc()
296             msg = ' Could not purge items from topic %s\ntraceback:\n%s' \
297                     % (self.boundjid, error)
298             self.error(msg)
299
300     def subscribe(self, node):
301         """ Subscribe to a topic
302
303         :param node: Name of the topic
304         :type node: str
305
306         """
307         try:
308             result = self['xep_0060'].subscribe(self._server, node)
309             msg = ' Subscribed %s to topic %s' \
310                     % (self.boundjid.user, node)
311             #self.info(msg)
312             self.debug(msg)
313         except:
314             error = traceback.format_exc()
315             msg = ' Could not subscribe %s to topic %s\ntraceback:\n%s' \
316                     % (self.boundjid.bare, node, error)
317             self.error(msg)
318
319     def unsubscribe(self, node):
320         """ Unsubscribe to a topic
321
322         :param node: Name of the topic
323         :type node: str
324
325         """
326         try:
327             result = self['xep_0060'].unsubscribe(self._server, node)
328             msg = ' Unsubscribed %s from topic %s' % (self.boundjid.bare, node)
329             self.debug(msg)
330         except:
331             error = traceback.format_exc()
332             msg = ' Could not unsubscribe %s from topic %s\ntraceback:\n%s' \
333                     % (self.boundjid.bare, node, error)
334             self.error(msg)
335
336     def check_mailbox(self, itype, attr):
337         """ Check the mail box
338
339         :param itype: type of mail
340         :type itype: str
341         :param attr: value wanted
342         :type attr: str
343
344         """
345         return self._parser.check_mailbox(itype, attr)
346
347
348     def handle_omf_message(self, iq):
349         """ Handle published/received message 
350
351         :param iq: Stanzas that is currently published/received
352         :type iq: Iq Stanza
353
354         """
355         self._parser.handle(iq)
356