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