Modified FailureManager to abort only when critical resources fail
[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, failtrap
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     @failtrap
151     def deploy(self):
152         """ Deploy the RM. It means nothing special for an application 
153         for now (later it will be upload sources, ...)
154         It becomes DEPLOYED after getting the xmpp client.
155
156         """
157         self.set('xmppSlice', self.node.get('xmppSlice'))
158         self.set('xmppHost', self.node.get('xmppHost'))
159         self.set('xmppPort', self.node.get('xmppPort'))
160         self.set('xmppPassword', self.node.get('xmppPassword'))
161
162         if not self._omf_api :
163             self._omf_api = OMFAPIFactory.get_api(self.get('xmppSlice'), 
164                 self.get('xmppHost'), self.get('xmppPort'), 
165                 self.get('xmppPassword'), exp_id = self.exp_id)
166
167         if not self._omf_api :
168             msg = "Credentials are not initialzed. XMPP Connections impossible"
169             self.error(msg)
170             raise RuntimeError, msg
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     @failtrap
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             raise RuntimeError, msg
191
192         if not self.get('args'):
193             self.set('args', " ")
194         if not self.get('env'):
195             self.set('env', " ")
196
197         # Some information to check the information in parameter
198         msg = " " + self.rtype() + " ( Guid : " + str(self._guid) +") : " + \
199             self.get('appid') + " : " + self.get('path') + " : " + \
200             self.get('args') + " : " + self.get('env')
201         self.info(msg)
202
203         try:
204             self._omf_api.execute(self.node.get('hostname'),self.get('appid'), \
205                 self.get('args'), self.get('path'), self.get('env'))
206         except AttributeError:
207             msg = "Credentials are not initialzed. XMPP Connections impossible"
208             self.error(msg)
209             raise
210
211         super(OMFApplication, self).start()
212
213     @failtrap
214     def stop(self):
215         """ Stop the RM. It means : Send Xmpp Message Using OMF protocol to 
216         kill the application. 
217         State is set to STOPPED after the message is sent.
218
219         """
220         try:
221             self._omf_api.exit(self.node.get('hostname'),self.get('appid'))
222         except AttributeError:
223             msg = "Credentials were not initialzed. XMPP Connections impossible"
224             self.error(msg)
225             raise
226
227         super(OMFApplication, self).stop()
228         self.set_finished()
229
230     def release(self):
231         """ Clean the RM at the end of the experiment and release the API.
232
233         """
234         try:
235             if self._omf_api :
236                 OMFAPIFactory.release_api(self.get('xmppSlice'), 
237                     self.get('xmppHost'), self.get('xmppPort'), 
238                     self.get('xmppPassword'), exp_id = self.exp_id)
239         except:
240             import traceback
241             err = traceback.format_exc()
242             self.error(err)
243
244         super(OMFApplication, self).release()
245