2 # NEPI, a framework to manage network experiments
3 # Copyright (C) 2013 INRIA
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.
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.
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/>.
18 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
19 # Julien Tribino <julien.tribino@inria.fr>
21 from nepi.util.logger import Logger
26 from sleekxmpp.exceptions import IqError, IqTimeout
27 class BaseOMFClient(sleekxmpp.ClientXMPP):
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"+ \
35 "sudo python setup.py install\n"
36 class BaseOMFClient(object):
40 import xml.etree.ElementTree as ET
42 # inherit from BaseXmpp and XMLstream classes
43 class OMFClient(sleekxmpp.ClientXMPP, Logger):
45 .. class:: Class Args :
47 :param jid: Jabber Id (= Xmpp Slice + Date)
49 :param password: Jabber Password (= Xmpp Password)
54 This class is an XMPP Client with customized method
58 def __init__(self, jid, password):
61 :param jid: Jabber Id (= Xmpp Slice + Date)
63 :param password: Jabber Password (= Xmpp Password)
68 Logger.__init__(self, "OMFClient")
70 sleekxmpp.ClientXMPP.__init__(self, jid, password)
72 self._registered = False
75 self.register_plugin('xep_0077') # In-band registration
76 self.register_plugin('xep_0030')
77 self.register_plugin('xep_0059')
78 self.register_plugin('xep_0060') # PubSub
80 self.add_event_handler("session_start", self.start)
81 self.add_event_handler("register", self.register)
82 self.add_event_handler("pubsub_publish", self.handle_omf_message)
86 """ Check if the client is ready
91 def start(self, event):
92 """ Send presence to the Xmppp Server. This function is called directly by the sleekXmpp library
97 self._server = "pubsub.%s" % self.boundjid.domain
99 def register(self, iq):
100 """ Register to the Xmppp Server. This function is called directly by the sleekXmpp library
104 msg = " %s already registered!" % self.boundjid
110 resp['register']['username'] = self.boundjid.user
111 resp['register']['password'] = self.password
115 msg = " Account created for %s!" % self.boundjid
117 self._registered = True
119 msg = " Could not register account: %s" % e.iq['error']['text']
122 msg = " No response from server."
125 def unregister(self):
126 """ Unregister from the Xmppp Server.
130 self.plugin['xep_0077'].cancel_registration(
131 ifrom=self.boundjid.full)
132 msg = " Account unregistered for %s!" % self.boundjid
135 msg = " Could not unregister account: %s" % e.iq['error']['text']
138 msg = " No response from server."
142 """ Get all the nodes of the Xmppp Server.
146 result = self['xep_0060'].get_nodes(self._server)
147 for item in result['disco_items']['items']:
148 msg = ' - %s' % str(item)
152 error = traceback.format_exc()
153 msg = 'Could not retrieve node list.\ntraceback:\n%s' % error
156 def subscriptions(self):
157 """ Get all the subscriptions of the Xmppp Server.
161 result = self['xep_0060'].get_subscriptions(self._server)
163 for node in result['node']:
164 msg = ' - %s' % str(node)
168 error = traceback.format_exc()
169 msg = ' Could not retrieve subscriptions.\ntraceback:\n%s' % error
172 def create(self, node):
173 """ Create the topic corresponding to the node
175 :param node: Name of the topic, corresponding to the node (ex : omf.plexus.wlab17)
179 msg = " Create Topic : " + node
182 config = self['xep_0004'].makeForm('submit')
183 config.add_field(var='pubsub#node_type', value='leaf')
184 config.add_field(var='pubsub#notify_retract', value='0')
185 config.add_field(var='pubsub#publish_model', value='open')
186 config.add_field(var='pubsub#persist_items', value='1')
187 config.add_field(var='pubsub#max_items', value='1')
188 config.add_field(var='pubsub#title', value=node)
191 self['xep_0060'].create_node(self._server, node, config = config)
193 error = traceback.format_exc()
194 msg = ' Could not create topic: %s\ntraceback:\n%s' % (node, error)
197 def delete(self, node):
198 """ Delete the topic corresponding to the node
200 :param node: Name of the topic, corresponding to the node (ex : omf.plexus.wlab17)
204 # To check if the queue are well empty at the end
205 #print " length of the queue : " + str(self.send_queue.qsize())
206 #print " length of the queue : " + str(self.event_queue.qsize())
208 self['xep_0060'].delete_node(self._server, node)
209 msg = ' Deleted node: %s' % node
212 error = traceback.format_exc()
213 msg = ' Could not delete topic: %s\ntraceback:\n%s' % (node, error)
216 def publish(self, data, node):
217 """ Publish the data to the corresponding topic
219 :param data: Data that will be published
221 :param node: Name of the topic
226 msg = " Publish to Topic : " + node
229 result = self['xep_0060'].publish(self._server,node,payload=data)
230 # id = result['pubsub']['publish']['item']['id']
231 # print('Published at item id: %s' % id)
233 error = traceback.format_exc()
234 msg = ' Could not publish to: %s\ntraceback:\n%s' % (node, error)
240 :param data: data from which the items will be get back
246 result = self['xep_0060'].get_item(self._server, self.boundjid,
248 for item in result['pubsub']['items']['substanzas']:
249 msg = 'Retrieved item %s: %s' % (item['id'], tostring(item['payload']))
252 error = traceback.format_exc()
253 msg = ' Could not retrieve item %s from topic %s\ntraceback:\n%s' \
254 % (data, self.boundjid, error)
257 def retract(self, data):
260 :param data: data from which the item will be retracted
265 result = self['xep_0060'].retract(self._server, self.boundjid, data)
266 msg = ' Retracted item %s from topic %s' % (data, self.boundjid)
269 error = traceback.format_exc()
270 msg = 'Could not retract item %s from topic %s\ntraceback:\n%s' \
271 % (data, self.boundjid, error)
275 """ Purge the information in the server
279 result = self['xep_0060'].purge(self._server, self.boundjid)
280 msg = ' Purged all items from topic %s' % self.boundjid
283 error = traceback.format_exc()
284 msg = ' Could not purge items from topic %s\ntraceback:\n%s' \
285 % (self.boundjid, error)
288 def subscribe(self, node):
289 """ Subscribe to a topic
291 :param node: Name of the topic
296 result = self['xep_0060'].subscribe(self._server, node)
297 msg = ' Subscribed %s to topic %s' \
298 % (self.boundjid.user, node)
302 error = traceback.format_exc()
303 msg = ' Could not subscribe %s to topic %s\ntraceback:\n%s' \
304 % (self.boundjid.bare, node, error)
307 def unsubscribe(self, node):
308 """ Unsubscribe to a topic
310 :param node: Name of the topic
315 result = self['xep_0060'].unsubscribe(self._server, node)
316 msg = ' Unsubscribed %s from topic %s' % (self.boundjid.bare, node)
319 error = traceback.format_exc()
320 msg = ' Could not unsubscribe %s from topic %s\ntraceback:\n%s' \
321 % (self.boundjid.bare, node, error)
324 def _check_for_tag(self, root, namespaces, tag):
325 """ Check if an element markup is in the ElementTree
327 :param root: Root of the tree
328 :type root: ElementTree Element
329 :param namespaces: Namespaces of the element
330 :type namespaces: str
331 :param tag: Tag that will search in the tree
335 for element in root.iter(namespaces+tag):
341 def _check_output(self, root, namespaces):
342 """ Check the significative element in the answer and display it
344 :param root: Root of the tree
345 :type root: ElementTree Element
346 :param namespaces: Namespaces of the tree
347 :type namespaces: str
350 fields = ["TARGET", "REASON", "PATH", "APPID", "VALUE"]
353 msg = self._check_for_tag(root, namespaces, elt)
355 response = response + " " + msg.text + " :"
356 deb = self._check_for_tag(root, namespaces, "MESSAGE")
358 msg = response + " " + deb.text
363 def handle_omf_message(self, iq):
364 """ Handle published/received message
366 :param iq: Stanzas that is currently published/received
370 namespaces = "{http://jabber.org/protocol/pubsub}"
371 for i in iq['pubsub_event']['items']:
372 root = ET.fromstring(str(i))
373 self._check_output(root, namespaces)