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