1
2 """A XMLRPC-Server that answeres about the current state of a Foam-Run"""
3
4 from ServerBase import ServerBase
5
6 from xmlrpclib import ServerProxy
7 from time import sleep
8 from random import random
9
10 from PyFoam import configuration as config
11 from PyFoam import versionString
12 from PyFoam.Basics.RingBuffer import RingBuffer
13 from PyFoam.Infrastructure.NetworkHelpers import freeServerPort
14 from PyFoam.Infrastructure.Logging import foamLogger
15 from PyFoam.FoamInformation import foamMPI
16 from PyFoam.RunDictionary.ParameterFile import ParameterFile
17 from PyFoam.Error import warning
18
19 from Hardcoded import userName
20
21 from threading import Lock,Thread,Timer
22 from time import time
23 from os import environ,uname,path,getpid
24 import socket
25
26 import sys,string
27 from traceback import extract_tb
28
30 """Finds a free server port on this machine and returns it
31
32 Valid server ports are in the range 18000 upward (the function tries to
33 find the lowest possible port number
34
35 ATTENTION: this part may introduce race conditions"""
36
37 return freeServerPort(config().getint("Network","startServerPort"),
38 length=config().getint("Network","nrServerPorts"))
39
41 """The class that handles the actual requests (only needed to hide the
42 Thread-methods from the world
43 """
44 - def __init__(self,run=None,master=None,lines=100):
45 """
46 @param run: The thread that controls the run
47 @param master: The Runner-Object that controls everything
48 @param lines: the number of lines the server should remember
49 """
50 self._run=run
51 self._master=master
52 self._lines=RingBuffer(nr=lines)
53 self._lastTime=time()
54 self._linesLock=Lock()
55 self._maxOutputTime=config().getfloat("IsAlive","maxTimeStart")
56
58 """Inserts a new line, not to be called via XMLRPC"""
59 self._linesLock.acquire()
60 self._lines.insert(line)
61 tmp=time()
62 if (tmp-self._lastTime)>self._maxOutputTime:
63 self._maxOutputTime=tmp-self._lastTime
64 self._lastTime=tmp
65 self._linesLock.release()
66
68 """This is a Foam-Server (True by default)"""
69 return True
70
72 """The calculation still generates output and therefor seems to be living"""
73 return self.elapsedTime()<self._maxOutputTime
74
76 """Interrupts the FOAM-process"""
77 if self._run:
78 foamLogger().warning("Killed by request")
79 self._run.interrupt()
80 return True
81 else:
82 return False
83
85 """Stops the run gracefully (after writing the last time-step to disk)"""
86 self._master.stopGracefully()
87 return True
88
90 """Makes the program write the next time-step to disk and the continue"""
91 self._master.writeResults()
92 return True
93
95 """Argument vector with which the runner was called"""
96 if self._master:
97 return self._master.origArgv
98 else:
99 return []
100
102 """Argument vector with which the runner started the run"""
103 if self._master:
104 return self._master.argv
105 else:
106 return []
107
109 """Is it a parallel run?"""
110 if self._master:
111 return self._master.lam!=None
112 else:
113 return False
114
116 """How many processors are used?"""
117 if self._master:
118 if self._master.lam!=None:
119 return self._master.lam.cpuNr()
120 else:
121 return 1
122 else:
123 return 0
124
126 """Number of warnings the executable emitted"""
127 if self._master:
128 return self._master.warnings
129 else:
130 return 0
131
133 """The command line"""
134 if self._master:
135 return string.join(self._master.origArgv)
136 else:
137 return ""
138
140 """The actual command line used"""
141 if self._master:
142 return self._master.cmd
143 else:
144 return ""
145
147 """Name of the Python-Script that runs the show"""
148 return sys.argv[0]
149
151 """@return: the last line that was output by the running FOAM-process"""
152 self._linesLock.acquire()
153 result=self._lines.last()
154 self._linesLock.release()
155 if not result:
156 return ""
157 return result
158
160 """@return: the current last lines as a string"""
161 self._linesLock.acquire()
162 tmp=self._lines.dump()
163 self._linesLock.release()
164 result=""
165 for l in tmp:
166 result+=l
167
168 return result
169
171 """@return: time in seconds since the last line was output"""
172 self._linesLock.acquire()
173 result=time()-self._lastTime
174 self._linesLock.release()
175
176 return result
177
179 """@param name: name of an environment variable
180 @return: value of the variable, empty string if non-existing"""
181 result=""
182 if environ.has_key(name):
183 result=environ[name]
184 return result
185
187 """@return: name of the MPI-implementation"""
188 return foamMPI()
189
191 """Version number of the Foam-Version"""
192 return self.getEnviron("WM_PROJECT_VERSION")
193
195 """@return: Version number of the PyFoam"""
196 return versionString()
197
199 """@return: the complete uname-information"""
200 return uname()
201
203 """@return: the ip of this machine"""
204 return socket.gethostbyname(socket.gethostname())
205
207 """@return: The name of the computer"""
208 return uname()[1]
209
211 """@return: all the configured parameters"""
212 return config().dump()
213
215 """@return: the current working directory"""
216 return path.abspath(path.curdir)
217
219 """@return: the PID of the script"""
220 return getpid()
221
223 """@return: the user that runs this script"""
224 return userName()
225
227 """@return: an ID for this run: IP and process-id"""
228 return "%s:%d" % (self.ip(),self.pid())
229
231 """@return: the current time in the simulation"""
232 if self._master.nowTime:
233 return self._master.nowTime
234 else:
235 return 0
236
238 """@return: the time in the simulation for which the mesh was created"""
239 if self._master.nowTime:
240 return self._master.createTime
241 else:
242 return 0
243
250
252 """@return: parameter startTime from the controlDict"""
253 return float(self._readParameter("startTime"))
254
256 """@return: parameter endTime from the controlDict"""
257 return float(self._readParameter("endTime"))
258
260 """@return: parameter startTime from the controlDict"""
261 return float(self._readParameter("deltaT"))
262
264 """This is the class that serves the requests about the FOAM-Run"""
265 - def __init__(self,run=None,master=None,lines=100):
266 """
267 @param run: The thread that controls the run
268 @param master: The Runner-Object that controls everything
269 @param lines: the number of lines the server should remember
270 """
271 Thread.__init__(self)
272
273 tries=0
274 maxTries=length=config().getint("Network","socketRetries")
275
276 ok=False
277
278 while not ok and tries<maxTries:
279 ok=True
280 tries+=1
281
282 self._port=findFreePort()
283
284 self._running=False
285
286 if self._port<0:
287 foamLogger().warning("Could not get a free port. Server not started")
288 return
289
290 try:
291 foamLogger().info("Serving on port %d" % self._port)
292 self._server=ServerBase(('',self._port),logRequests=False)
293 self._server.register_introspection_functions()
294 self._answerer=FoamAnswerer(run=run,master=master,lines=lines)
295 self._server.register_instance(self._answerer)
296 self._server.register_function(self.killServer)
297 self._server.register_function(self.kill)
298 if run:
299 self._server.register_function(run.cpuTime)
300 self._server.register_function(run.cpuUserTime)
301 self._server.register_function(run.cpuSystemTime)
302 self._server.register_function(run.wallTime)
303 self._server.register_function(run.usedMemory)
304 except socket.error,reason:
305 ok=False
306 warning("Could not start on port",self._port,"althoug it was promised. Try:",tries,"of",maxTries)
307 foamLogger().warning("Could not get port %d - SocketError: %s. Try %d of %d" % (self._port,str(reason),tries,maxTries))
308 sleep(2+20*random())
309
310 if not ok:
311 foamLogger().warning("Exceeded maximum number of tries for getting a port: %d" % maxTries)
312 warning("Did not get a port after %d tries" % tries)
313 else:
314 if tries>1:
315 warning("Got a port after %d tries" % tries)
316
318 if self._port<0:
319 return
320
321 reg=Timer(5.,self.register)
322 reg.start()
323
324 self._running=True
325
326 while self._running:
327 self._server.handle_request()
328
329
330 self._server.server_close()
331
332 foamLogger().info("Stopped serving on port %d" % self._port)
333
335 """Returns the IP, the PID and the port of the server (as one tuple)"""
336
337 return self._answerer.ip(),self._answerer.pid(),self._port
338
340 """Interrupts the FOAM-process (and kills the server)"""
341 self._answerer._kill()
342 return self.killServer()
343
345 """Kills the server process"""
346 tmp=self._running
347 self._running=False
348 return tmp
349
351 """Tries to register with the Meta-Server"""
352
353 try:
354 try:
355 meta=ServerProxy("http://%s:%d" % (config().get("Metaserver","ip"),config().getint("Metaserver","port")))
356 meta.registerServer(self._answerer.ip(),self._answerer.pid(),self._port)
357 except socket.error, reason:
358 foamLogger().warning("Can't connect to meta-server - SocketError: "+str(reason))
359 except:
360 foamLogger().error("Can't connect to meta-server - Unknown Error: "+str(sys.exc_info()[0]))
361 foamLogger().error(str(sys.exc_info()[1]))
362 foamLogger().error("Traceback: "+str(extract_tb(sys.exc_info()[2])))
363 except:
364
365 pass
366
368 """Tries to deregister with the Meta-Server"""
369 self._server.server_close()
370
371 try:
372 meta=ServerProxy("http://%s:%d" % (config().get("Metaserver","ip"),config().getint("Metaserver","port")))
373 meta.deregisterServer(self._answerer.ip(),self._answerer.pid(),self._port)
374 except socket.error, reason:
375 foamLogger().warning("Can't connect to meta-server - SocketError: "+str(reason))
376 except:
377 foamLogger().error("Can't connect to meta-server - Unknown Error: "+str(sys.exc_info()[0]))
378 foamLogger().error(str(sys.exc_info()[1]))
379 foamLogger().error("Traceback: "+str(extract_tb(sys.exc_info()[2])))
380
382 """Inserts a new line, not to be called via XMLRPC"""
383 self._answerer._insertLine(line)
384