change bug of dict
[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             selferror(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.info(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.info(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         self.debug(" Create Topic : " + node)
165    
166         config = self['xep_0004'].makeForm('submit')
167         config.add_field(var='pubsub#node_type', value='leaf')
168         config.add_field(var='pubsub#notify_retract', value='0')
169         config.add_field(var='pubsub#publish_model', value='open')
170         config.add_field(var='pubsub#persist_items', value='1')
171         config.add_field(var='pubsub#max_items', value='1')
172         config.add_field(var='pubsub#title', value=node)
173
174         try:
175             self['xep_0060'].create_node(self._server, node, config = config)
176         except:
177             error = traceback.format_exc()
178             msg = ' Could not create topic: %s\ntraceback:\n%s' % (node, error)
179             self.error(msg)
180
181     def delete(self, node):
182         """  Delete the topic corresponding to the node
183
184         :param node: Name of the topic, corresponding to the node (ex : omf.plexus.wlab17)
185         :type node: str
186
187         """
188         # To check if the queue are well empty at the end
189         #print " length of the queue : " + str(self.send_queue.qsize())
190         #print " length of the queue : " + str(self.event_queue.qsize())
191         try:
192             self['xep_0060'].delete_node(self._server, node)
193             msg = ' Deleted node: %s' % node
194             self.info(msg)
195         except:
196             error = traceback.format_exc()
197             msg = ' Could not delete topic: %s\ntraceback:\n%s' % (node, error)
198             self.error(msg)
199     
200     def publish(self, data, node):
201         """  Publish the data to the corresponding topic
202
203         :param data: Data that will be published
204         :type data: str
205         :param node: Name of the topic
206         :type node: str
207
208         """ 
209
210         msg = " Publish to Topic : " + node
211         self.debug(msg)
212         try:
213             result = self['xep_0060'].publish(self._server,node,payload=data)
214             # id = result['pubsub']['publish']['item']['id']
215             # print('Published at item id: %s' % id)
216         except:
217             error = traceback.format_exc()
218             msg = ' Could not publish to: %s\ntraceback:\n%s' % (node, error)
219             self.error(msg)
220
221     def get(self, data):
222         """  Get the item
223
224         :param data: data from which the items will be get back
225         :type data: str
226
227
228         """
229         try:
230             result = self['xep_0060'].get_item(self._server, self.boundjid,
231                 data)
232             for item in result['pubsub']['items']['substanzas']:
233                 msg = 'Retrieved item %s: %s' % (item['id'], tostring(item['payload']))
234                 self.info(msg)
235         except:
236             error = traceback.format_exc()
237             msg = ' Could not retrieve item %s from topic %s\ntraceback:\n%s' \
238                     % (data, self.boundjid, error)
239             self.error(msg)
240
241     def retract(self, data):
242         """  Retract the item
243
244         :param data: data from which the item will be retracted
245         :type data: str
246
247         """
248         try:
249             result = self['xep_0060'].retract(self._server, self.boundjid, data)
250             msg = ' Retracted item %s from topic %s' % (data, self.boundjid)
251             self.info(msg)
252         except:
253             error = traceback.format_exc()
254             msg = 'Could not retract item %s from topic %s\ntraceback:\n%s' \
255                     % (data, self.boundjid, error)
256             self.error(msg)
257
258     def purge(self):
259         """  Purge the information in the server
260
261         """
262         try:
263             result = self['xep_0060'].purge(self._server, self.boundjid)
264             msg = ' Purged all items from topic %s' % self.boundjid
265             self.info(msg)
266         except:
267             error = traceback.format_exc()
268             msg = ' Could not purge items from topic %s\ntraceback:\n%s' \
269                     % (self.boundjid, error)
270             self.error(msg)
271
272     def subscribe(self, node):
273         """ Subscribe to a topic
274
275         :param node: Name of the topic
276         :type node: str
277
278         """
279         try:
280             result = self['xep_0060'].subscribe(self._server, node)
281             msg = ' Subscribed %s to topic %s' \
282                     % (self.boundjid.user, node)
283             self.info(msg)
284             #self.debug(msg)
285         except:
286             error = traceback.format_exc()
287             msg = ' Could not subscribe %s to topic %s\ntraceback:\n%s' \
288                     % (self.boundjid.bare, node, error)
289             self.error(msg)
290
291     def unsubscribe(self, node):
292         """ Unsubscribe to a topic
293
294         :param node: Name of the topic
295         :type node: str
296
297         """
298         try:
299             result = self['xep_0060'].unsubscribe(self._server, node)
300             msg = ' Unsubscribed %s from topic %s' % (self.boundjid.bare, node)
301             self.info(msg)
302         except:
303             error = traceback.format_exc()
304             msg = ' Could not unsubscribe %s from topic %s\ntraceback:\n%s' \
305                     % (self.boundjid.bare, node, error)
306             self.error(msg)
307
308     def _check_for_tag(self, root, namespaces, tag):
309         """  Check if an element markup is in the ElementTree
310
311         :param root: Root of the tree
312         :type root: ElementTree Element
313         :param namespaces: Namespaces of the element
314         :type namespaces: str
315         :param tag: Tag that will search in the tree
316         :type tag: str
317
318         """
319         for element in root.iter(namespaces+tag):
320             if element.text:
321                 return element
322             else : 
323                 return None    
324
325     def _check_output(self, root, namespaces):
326         """ Check the significative element in the answer and display it
327
328         :param root: Root of the tree
329         :type root: ElementTree Element
330         :param namespaces: Namespaces of the tree
331         :type namespaces: str
332
333         """
334         fields = ["TARGET", "REASON", "PATH", "APPID", "VALUE"]
335         response = ""
336         for elt in fields:
337             msg = self._check_for_tag(root, namespaces, elt)
338             if msg is not None:
339                 response = response + " " + msg.text + " :"
340         deb = self._check_for_tag(root, namespaces, "MESSAGE")
341         if deb is not None:
342             msg = response + " " + deb.text
343             self.debug(msg)
344         else :
345             self.info(response)
346
347     def handle_omf_message(self, iq):
348         """ Handle published/received message 
349
350         :param iq: Stanzas that is currently published/received
351         :type iq: Iq Stanza
352
353         """
354         namespaces = "{http://jabber.org/protocol/pubsub}"
355         for i in iq['pubsub_event']['items']:
356             root = ET.fromstring(str(i))
357             self._check_output(root, namespaces)
358
359