update omf part with last changes for the demo
[nepi.git] / src / nepi / resources / omf / application.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 from nepi.execution.resource import ResourceManager, clsinit_copy, ResourceState, \
22         reschedule_delay
23 from nepi.execution.attribute import Attribute, Flags 
24 from nepi.resources.omf.omf_resource import ResourceGateway, OMFResource
25 from nepi.resources.omf.node import OMFNode
26 from nepi.resources.omf.omf_api import OMFAPIFactory
27
28 #from nepi.util.sshfuncs import ProcStatus
29 from nepi.util import sshfuncs
30
31
32 @clsinit_copy
33 class OMFApplication(OMFResource):
34     """
35     .. class:: Class Args :
36       
37         :param ec: The Experiment controller
38         :type ec: ExperimentController
39         :param guid: guid of the RM
40         :type guid: int
41         :param creds: Credentials to communicate with the rm (XmppClient)
42         :type creds: dict
43
44     .. note::
45
46        This class is used only by the Experiment Controller through the 
47        Resource Factory
48
49     """
50     _rtype = "OMFApplication"
51     _authorized_connections = ["OMFNode"]
52
53     @classmethod
54     def _register_attributes(cls):
55         """ Register the attributes of an OMF application
56
57         """
58         appid = Attribute("appid", "Name of the application")
59         path = Attribute("path", "Path of the application")
60         args = Attribute("args", "Argument of the application")
61         env = Attribute("env", "Environnement variable of the application")
62         stdin = Attribute("stdin", "Input of the application", default = "")
63         sources = Attribute("sources", "Sources of the application", 
64                      flags = Flags.ExecReadOnly)
65         sshuser = Attribute("sshUser", "user to connect with ssh", 
66                      flags = Flags.ExecReadOnly)
67         sshkey = Attribute("sshKey", "key to use for ssh", 
68                      flags = Flags.ExecReadOnly)
69         cls._register_attribute(appid)
70         cls._register_attribute(path)
71         cls._register_attribute(args)
72         cls._register_attribute(env)
73         cls._register_attribute(stdin)
74         cls._register_attribute(sources)
75         cls._register_attribute(sshuser)
76         cls._register_attribute(sshkey)
77
78     def __init__(self, ec, guid):
79         """
80         :param ec: The Experiment controller
81         :type ec: ExperimentController
82         :param guid: guid of the RM
83         :type guid: int
84         :param creds: Credentials to communicate with the rm (XmppClient for OMF)
85         :type creds: dict
86
87         """
88         super(OMFApplication, self).__init__(ec, guid)
89
90         self.set('appid', "")
91         self.set('path', "")
92         self.set('args', "")
93         self.set('env', "")
94
95         self._node = None
96
97         self._omf_api = None
98
99         self.add_set_hook()
100
101     @property
102     def exp_id(self):
103         return self.ec.exp_id
104
105     @property
106     def node(self):
107         rm_list = self.get_connected(OMFNode.rtype())
108         if rm_list: return rm_list[0]
109         return None
110
111     def stdin_hook(self, old_value, new_value):
112         self._omf_api.send_stdin(self.node.get('hostname'), new_value, self.get('appid'))
113         return new_value
114
115     def add_set_hook(self):
116         attr = self._attrs["stdin"]
117         attr.set_hook = self.stdin_hook
118
119     def valid_connection(self, guid):
120         """ Check if the connection with the guid in parameter is possible. 
121         Only meaningful connections are allowed.
122
123         :param guid: Guid of RM it will be connected
124         :type guid: int
125         :rtype:  Boolean
126
127         """
128         rm = self.ec.get_resource(guid)
129         if rm.rtype() not in self._authorized_connections:
130             msg = ("Connection between %s %s and %s %s refused: "
131                     "An Application can be connected only to a Node" ) % \
132                 (self.rtype(), self._guid, rm.rtype(), guid)
133             self.debug(msg)
134
135             return False
136
137         elif len(self.connections) != 0 :
138             msg = ("Connection between %s %s and %s %s refused: "
139                     "This Application is already connected" ) % \
140                 (self.rtype(), self._guid, rm.rtype(), guid)
141             self.debug(msg)
142             return False
143         else :
144             msg = "Connection between %s %s and %s %s accepted" % (
145                     self.rtype(), self._guid, rm.rtype(), guid)
146             self.debug(msg)
147
148             return True
149
150     def deploy(self):
151         """ Deploy the RM. It means nothing special for an application 
152         for now (later it will be upload sources, ...)
153         It becomes DEPLOYED after getting the xmpp client.
154
155         """
156         self.set('xmppSlice', self.node.get('xmppSlice'))
157         self.set('xmppHost', self.node.get('xmppHost'))
158         self.set('xmppPort', self.node.get('xmppPort'))
159         self.set('xmppPassword', self.node.get('xmppPassword'))
160
161         if not self._omf_api :
162             self._omf_api = OMFAPIFactory.get_api(self.get('xmppSlice'), 
163                 self.get('xmppHost'), self.get('xmppPort'), 
164                 self.get('xmppPassword'), exp_id = self.exp_id)
165
166         if not self._omf_api :
167             msg = "Credentials are not initialzed. XMPP Connections impossible"
168             self.error(msg)
169             self.fail()
170             return
171
172         if self.get('sources'):
173             gateway = ResourceGateway.AMtoGateway[self.get('xmppHost')]
174             user = self.get('sshUser') or self.get('xmppSlice')
175             dst = user + "@"+ gateway + ":"
176             (out, err), proc = sshfuncs.rcopy(self.get('sources'), dst)
177
178         super(OMFApplication, self).deploy()
179
180
181     def start(self):
182         """ Start the RM. It means : Send Xmpp Message Using OMF protocol 
183          to execute the application. 
184          It becomes STARTED before the messages are sent (for coordination)
185
186         """
187         if not (self.get('appid') and self.get('path')) :
188             msg = "Application's information are not initialized"
189             self.error(msg)
190             self.fail()
191             return
192
193         if not self.get('args'):
194             self.set('args', " ")
195         if not self.get('env'):
196             self.set('env', " ")
197
198         # Some information to check the information in parameter
199         msg = " " + self.rtype() + " ( Guid : " + str(self._guid) +") : " + \
200             self.get('appid') + " : " + self.get('path') + " : " + \
201             self.get('args') + " : " + self.get('env')
202         self.info(msg)
203
204         try:
205             self._omf_api.execute(self.node.get('hostname'),self.get('appid'), \
206                 self.get('args'), self.get('path'), self.get('env'))
207         except AttributeError:
208             msg = "Credentials are not initialzed. XMPP Connections impossible"
209             self.error(msg)
210             self.fail()
211             raise
212
213         super(OMFApplication, self).start()
214
215     def stop(self):
216         """ Stop the RM. It means : Send Xmpp Message Using OMF protocol to 
217         kill the application. 
218         State is set to STOPPED after the message is sent.
219
220         """
221         try:
222             self._omf_api.exit(self.node.get('hostname'),self.get('appid'))
223         except AttributeError:
224             msg = "Credentials were not initialzed. XMPP Connections impossible"
225             self.error(msg)
226             self.fail()
227             return
228
229         super(OMFApplication, self).stop()
230         self.set_finished()
231
232     def release(self):
233         """ Clean the RM at the end of the experiment and release the API.
234
235         """
236         if self._omf_api :
237             OMFAPIFactory.release_api(self.get('xmppSlice'), 
238                 self.get('xmppHost'), self.get('xmppPort'), 
239                 self.get('xmppPassword'), exp_id = self.exp_id)
240
241         super(OMFApplication, self).release()
242