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