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