048117d5264a19891e5cfd2fdc3b1edbb069dbae
[nepi.git] / src / nepi / resources / omf / omf6_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 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 import ssl
22 import sys
23 import time
24
25 from nepi.util.timefuncs import tsformat 
26 import os
27
28 from nepi.util.logger import Logger
29
30 from nepi.resources.omf.omf_client import OMFClient
31 from nepi.resources.omf.messages_6 import MessageHandler
32
33 class OMF6API(Logger):
34     """
35     .. class:: Class Args :
36       
37         :param server: Xmpp Server
38         :type server: str
39         :param user: Xmpp User
40         :type user: str
41         :param port: Xmpp Port
42         :type port: str
43         :param password: Xmpp password
44         :type password: str
45         :param xmpp_root: Root of the Xmpp Topic Architecture
46         :type xmpp_root: str
47
48     .. note::
49
50        This class is the implementation of an OMF 5.4 API. 
51        Since the version 5.4.1, the Topic Architecture start with OMF_5.4 
52        instead of OMF used for OMF5.3
53
54     """
55     def __init__(self, server, user = "nepi", port="5222", password="1234",
56             exp_id = None):
57         """
58         :param server: Xmpp Server
59         :type server: str
60         :param user: Xmpp User
61         :type user: str
62         :param port: Xmpp Port
63         :type port: str
64         :param password: Xmpp password
65         :type password: str
66         :param xmpp_root: Root of the Xmpp Topic Architecture
67         :type xmpp_root: str
68
69         """
70         super(OMF6API, self).__init__("OMF6API")
71         self._exp_id = exp_id
72         self._user = user # name of the machine that run Nepi
73         self._server = server # name of the xmpp server
74         self._port = port # port of the xmpp server
75         self._password = password # password to connect to xmpp
76         self._jid = "%s-%s@%s" % (self._user, self._exp_id, self._server)
77         self._src = "xmpp://" + self._jid
78         
79         self._topics = []
80
81         # OMF xmpp client
82         self._client = None
83
84         # message handler
85         self._message = None
86
87         if sys.version_info < (3, 0):
88             reload(sys)
89             sys.setdefaultencoding('utf8')
90
91         # instantiate the xmpp client
92         self._init_client()
93
94         # register nepi topic
95         self._enroll_nepi()
96
97
98     def _init_client(self):
99         """ Initialize XMPP Client
100
101         """
102         xmpp = OMFClient(self._jid, self._password)
103         # PROTOCOL_SSLv3 required for compatibility with OpenFire
104         xmpp.ssl_version = ssl.PROTOCOL_SSLv3
105
106         if xmpp.connect((self._server, self._port)):
107             xmpp.process(block=False)
108             self.check_ready(xmpp)
109             self._client = xmpp
110             self._message = MessageHandler()
111         else:
112             msg = "Unable to connect to the XMPP server."
113             self.error(msg)
114             raise RuntimeError(msg)
115
116     def check_ready(self, xmpp):
117         delay = 1.0
118         for i in xrange(4):
119             if xmpp.ready:
120                 break
121             else:
122                 time.sleep(delay)
123                 delay = delay * 1.5
124         else:
125             msg = "XMPP Client is not ready after long time"
126             self.error(msg, out, err)
127             raise RuntimeError, msg
128
129     @property
130     def _nepi_topic(self):
131         """ Return the name of the session topic
132
133         """
134         msg = "nepi-" + self._exp_id
135         self.debug(msg)
136         return msg
137
138     def _enroll_nepi(self):
139         """ Create and Subscribe to the session Topic
140
141         """
142         nepi_topic = self._nepi_topic
143         self._client.create(nepi_topic)
144         self._client.subscribe(nepi_topic)
145
146
147     def create_and_enroll_topic(self, topic):
148         """ Create and Subscribe to the session topic and the resources
149             corresponding to the hostname
150
151         :param hostname: Full hrn of the node
152         :type hostname: str
153
154         """
155         if topic in self._topics:
156             return 
157
158         self._topics.append(topic)
159
160         self._client.create(topic)
161         self._client.subscribe(topic)
162
163
164     def enroll_topic(self, topic):
165         """ Create and Subscribe to the session topic and the resources
166             corresponding to the hostname
167
168         """
169         if topic in self._topics:
170             return 
171
172         self._topics.append(topic)
173         self._client.subscribe(topic)
174
175
176     def frcp_inform(self, topic, cid, itype):
177         """ Publish an inform message
178
179         """
180         msg_id = os.urandom(16).encode('hex')
181         timestamp = tsformat()
182         payload = self._message.inform_function(msg_id, self._src, timestamp, props = props ,guards = guards) 
183         
184         self._client.publish(payload, xmpp_node)
185
186     def frcp_configure(self, topic, props = None, guards = None ):
187         """ Publish a configure message
188
189         """
190         msg_id = os.urandom(16).encode('hex')
191         timestamp = tsformat()
192         payload = self._message.configure_function(msg_id, self._src, timestamp ,props = props ,guards = guards) 
193         self._client.publish(payload, topic)
194
195     
196     def frcp_create(self, msg_id, topic, rtype, props = None, guards = None ):
197         """ Publish a create message
198
199         """
200         timestamp = tsformat()
201         payload = self._message.create_function(msg_id, self._src, rtype, timestamp , props = props ,guards = guards) 
202         self._client.publish(payload, topic)
203
204
205     def frcp_request(self, topic, props = None, guards = None ):
206         """ Execute command on the node
207
208         """
209         msg_id = os.urandom(16).encode('hex')
210         timestamp = tsformat()
211         payload = self._message.request_function(msg_id, self._src, timestamp, props = props ,guards = guards) 
212         self._client.publish(payload, xmpp_node)
213
214     def frcp_release(self, msg_id, parent, child, res_id = None, props = None, guards = None ):
215         """ Publish a release message
216
217         """
218         timestamp = tsformat()
219         payload = self._message.release_function(msg_id, self._src, timestamp, res_id = res_id, props = props ,guards = guards) 
220         self._client.publish(payload, parent)
221
222         if child in self._topics:
223             self._topics.remove(child)
224
225         self._client.unsubscribe(child)
226         #self._client.delete(child)
227
228     def check_mailbox(self, itype, attr):
229         """ Check the mail box
230
231         :param itype: type of mail
232         :type itype: str
233         :param attr: value wanted
234         :type attr: str
235
236         """
237         return self._client.check_mailbox(itype, attr)
238
239     def unenroll_topic(self, topic):
240         """ Create and Subscribe to the session topic and the resources
241             corresponding to the hostname
242
243         """
244         if topic in self._topics:
245             self._topics.remove(topic)
246         self._client.unsubscribe(topic)
247
248     def disconnect(self) :
249         """ Delete the session and logger topics. Then disconnect 
250
251         """
252         self._client.delete(self._nepi_topic)
253        
254         # Wait the send queue to be empty before disconnect
255         self._client.disconnect(wait=True)
256         msg = " Disconnected from XMPP Server"
257         self.debug(msg)
258