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 import select
10
11 from PyFoam import configuration as config
12 from PyFoam import versionString
13 from PyFoam.Basics.RingBuffer import RingBuffer
14 from PyFoam.Infrastructure.NetworkHelpers import freeServerPort
15 from PyFoam.Infrastructure.Logging import foamLogger
16 from PyFoam.FoamInformation import foamMPI
17 from PyFoam.RunDictionary.ParameterFile import ParameterFile
18 from PyFoam.Error import warning
19 from PyFoam.Basics.GeneralPlotTimelines import allPlots
20 from PyFoam.Basics.TimeLineCollection import allLines
21
22 from Hardcoded import userName
23
24 from threading import Lock,Thread,Timer
25 from time import time
26 from os import environ,uname,path,getpid,getloadavg
27 import socket
28
29 import sys,string
30 from traceback import extract_tb
31
33 """Finds a free server port on this machine and returns it
34
35 Valid server ports are in the range 18000 upward (the function tries to
36 find the lowest possible port number
37
38 ATTENTION: this part may introduce race conditions"""
39
40 return freeServerPort(config().getint("Network","startServerPort"),
41 length=config().getint("Network","nrServerPorts"))
42
44 """The class that handles the actual requests (only needed to hide the
45 Thread-methods from the world
46 """
47 - def __init__(self,run=None,master=None,lines=100,foamserver=None):
48 """
49 @param run: The thread that controls the run
50 @param master: The Runner-Object that controls everything
51 @param lines: the number of lines the server should remember
52 """
53 self._run=run
54 self._master=master
55 self._foamserver=foamserver
56 self._lines=RingBuffer(nr=lines)
57 self._lastTime=time()
58 self._linesLock=Lock()
59 self._maxOutputTime=config().getfloat("IsAlive","maxTimeStart")
60
62 """Inserts a new line, not to be called via XMLRPC"""
63 self._linesLock.acquire()
64 self._lines.insert(line)
65 tmp=time()
66 if (tmp-self._lastTime)>self._maxOutputTime:
67 self._maxOutputTime=tmp-self._lastTime
68 self._lastTime=tmp
69 self._linesLock.release()
70
72 """This is a Foam-Server (True by default)"""
73 return True
74
76 """The calculation still generates output and therefor seems to be living"""
77 return self.elapsedTime()<self._maxOutputTime
78
80 """Interrupts the FOAM-process"""
81 if self._run:
82 foamLogger().warning("Killed by request")
83 self._run.interrupt()
84 return True
85 else:
86 return False
87
89 """Stops the run gracefully (after writing the last time-step to disk)"""
90 self._master.stopGracefully()
91 return True
92
94 """Makes the program write the next time-step to disk and the continue"""
95 self._master.writeResults()
96 return True
97
99 """Argument vector with which the runner was called"""
100 if self._master:
101 return self._master.origArgv
102 else:
103 return []
104
106 """Argument vector with which the runner started the run"""
107 if self._master:
108 return self._master.argv
109 else:
110 return []
111
113 """Is it a parallel run?"""
114 if self._master:
115 return self._master.lam!=None
116 else:
117 return False
118
120 """How many processors are used?"""
121 if self._master:
122 if self._master.lam!=None:
123 return self._master.lam.cpuNr()
124 else:
125 return 1
126 else:
127 return 0
128
130 """Number of warnings the executable emitted"""
131 if self._master:
132 return self._master.warnings
133 else:
134 return 0
135
137 """The command line"""
138 if self._master:
139 return string.join(self._master.origArgv)
140 else:
141 return ""
142
144 """The actual command line used"""
145 if self._master:
146 return self._master.cmd
147 else:
148 return ""
149
151 """Name of the Python-Script that runs the show"""
152 return sys.argv[0]
153
155 """@return: the time at which the last log-line was seen"""
156 return self._master.lastLogLineSeen
157
159 """@return: the time at which the last log-line was seen"""
160 return self._master.lastTimeStepSeen
161
163 """@return: the last line that was output by the running FOAM-process"""
164 self._linesLock.acquire()
165 result=self._lines.last()
166 self._linesLock.release()
167 if not result:
168 return ""
169 return result
170
172 """@return: the current last lines as a string"""
173 self._linesLock.acquire()
174 tmp=self._lines.dump()
175 self._linesLock.release()
176 result=""
177 for l in tmp:
178 result+=l
179
180 return result
181
183 """@return: time in seconds since the last line was output"""
184 self._linesLock.acquire()
185 result=time()-self._lastTime
186 self._linesLock.release()
187
188 return result
189
191 """@param name: name of an environment variable
192 @return: value of the variable, empty string if non-existing"""
193 result=""
194 if environ.has_key(name):
195 result=environ[name]
196 return result
197
199 """@return: name of the MPI-implementation"""
200 return foamMPI()
201
203 """Version number of the Foam-Version"""
204 return self.getEnviron("WM_PROJECT_VERSION")
205
207 """@return: Version number of the PyFoam"""
208 return versionString()
209
211 """@return: the complete uname-information"""
212 return uname()
213
215 """@return: the ip of this machine"""
216 try:
217 address = socket.gethostbyname(socket.gethostname())
218
219 except:
220 address = ''
221 if not address or address.startswith('127.'):
222
223 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
224 try:
225 s.connect(('4.2.2.1', 0))
226 address = s.getsockname()[0]
227 except:
228
229 address="127.0.0.1"
230 return address
231
233 """@return: The name of the computer"""
234 return uname()[1]
235
237 """@return: all the configured parameters"""
238 return config().dump()
239
241 """@return: the current working directory"""
242 return path.abspath(path.curdir)
243
245 """@return: the PID of the script"""
246 return getpid()
247
249 """@return: a tuple with the average loads of the last 1, 5 and 15 minutes"""
250 return getloadavg()
251
253 """@return: the user that runs this script"""
254 return userName()
255
257 """@return: an ID for this run: IP:port:startTimestamp """
258 return "%s:%d:%f" % (self.ip(),self._foamserver._port,self.startTimestamp())
259
261 """@return: the unix-timestamp of the process start"""
262 return self._master.startTimestamp
263
265 """@return: the current time in the simulation"""
266 if self._master.nowTime:
267 return self._master.nowTime
268 else:
269 return 0
270
272 """@return: the time in the simulation for which the mesh was created"""
273 if self._master.nowTime:
274 return self._master.createTime
275 else:
276 return 0
277
284
286 """@return: parameter startTime from the controlDict"""
287 return float(self._readParameter("startTime"))
288
290 """@return: parameter endTime from the controlDict"""
291 return float(self._readParameter("endTime"))
292
294 """@return: parameter startTime from the controlDict"""
295 return float(self._readParameter("deltaT"))
296
300
304
306 """@param time: name of the timestep
307 @return: list of the solution files at that timestep"""
308 return self._master.getSolutionDirectory()[time].getFiles()
309
314
315 - def getDictionaryText(self,directory,name):
316 """@param directory: Sub-directory of the case
317 @param name: name of the dictionary file
318 @return: the contents of the file as a big string"""
319 return self._master.getSolutionDirectory().getDictionaryText(directory,name)
320
321 - def getDictionaryContents(self,directory,name):
322 """@param directory: Sub-directory of the case
323 @param name: name of the dictionary file
324 @return: the contents of the file as a python data-structure"""
325 return self._master.getSolutionDirectory().getDictionaryContents(directory,name)
326
327 - def writeDictionaryText(self,directory,name,text):
328 """Writes the contents of a dictionary
329 @param directory: Sub-directory of the case
330 @param name: name of the dictionary file
331 @param text: String with the dictionary contents"""
332
333 self._master.getSolutionDirectory().writeDictionaryText(directory,name,text)
334
335 return True
336
337 - def writeDictionaryContents(self,directory,name,contents):
338 """Writes the contents of a dictionary
339 @param directory: Sub-directory of the case
340 @param name: name of the dictionary file
341 @param contents: Python-dictionary with the dictionary contents"""
342
343 self._master.getSolutionDirectory().writeDictionaryContents(directory,name,contents)
344 return True
345
349
353
355 """Checks whether there is a pending change to the controlDict"""
356 return self._master.controlDict == None
357
359 """Get the user-defined remark for this job"""
360 if self._master.remark:
361 return self._master.remark
362 else:
363 return ""
364
366 """Overwrite the user-defined remark
367 @return: True if the remark was set previously"""
368 oldRemark=self._master.remark
369 self._master.remark=remark
370 return oldRemark!=None
371
373 """Return the job-ID of the queuing-system. Empty if unset"""
374 if self._master.jobId:
375 return self._master.jobId
376 else:
377 return ""
378
380 """This is the class that serves the requests about the FOAM-Run"""
381 - def __init__(self,run=None,master=None,lines=100):
382 """
383 @param run: The thread that controls the run
384 @param master: The Runner-Object that controls everything
385 @param lines: the number of lines the server should remember
386 """
387 Thread.__init__(self)
388
389 self.isRegistered=False
390
391 tries=0
392 maxTries=length=config().getint("Network","socketRetries")
393
394 ok=False
395
396 while not ok and tries<maxTries:
397 ok=True
398 tries+=1
399
400 self._port=findFreePort()
401
402 self._running=False
403
404 if self._port<0:
405 foamLogger().warning("Could not get a free port. Server not started")
406 return
407
408 try:
409 foamLogger().info("Serving on port %d" % self._port)
410 self._server=ServerBase(('',self._port),logRequests=False)
411 self._server.register_introspection_functions()
412 self._answerer=FoamAnswerer(run=run,master=master,lines=lines,foamserver=self)
413 self._server.register_instance(self._answerer)
414 self._server.register_function(self.killServer)
415 self._server.register_function(self.kill)
416 if run:
417 self._server.register_function(run.cpuTime)
418 self._server.register_function(run.cpuUserTime)
419 self._server.register_function(run.cpuSystemTime)
420 self._server.register_function(run.wallTime)
421 self._server.register_function(run.usedMemory)
422 except socket.error,reason:
423 ok=False
424 warning("Could not start on port",self._port,"althoug it was promised. Try:",tries,"of",maxTries)
425 foamLogger().warning("Could not get port %d - SocketError: %s. Try %d of %d" % (self._port,str(reason),tries,maxTries))
426 sleep(2+20*random())
427
428 if not ok:
429 foamLogger().warning("Exceeded maximum number of tries for getting a port: %d" % maxTries)
430 warning("Did not get a port after %d tries" % tries)
431 else:
432 if tries>1:
433 warning("Got a port after %d tries" % tries)
434
436 foamLogger().info("Running server at port %d" % self._port)
437 if self._port<0:
438 return
439
440 reg=Timer(5.,self.register)
441 reg.start()
442
443 self._running=True
444
445 try:
446 while self._running:
447 self._server.handle_request()
448 except select.error,e:
449
450 pass
451
452
453 self._server.server_close()
454
455 foamLogger().warning("Stopped serving on port %d" % self._port)
456
458 """Returns the IP, the PID and the port of the server (as one tuple)"""
459
460 return self._answerer.ip(),self._answerer.pid(),self._port
461
463 """Interrupts the FOAM-process (and kills the server)"""
464 self._answerer._kill()
465 return self.killServer()
466
468 """Kills the server process"""
469 tmp=self._running
470 self._running=False
471 return tmp
472
474 """Tries to register with the Meta-Server"""
475
476 foamLogger().info("Trying to register as IP:%s PID:%d Port:%d" % (self._answerer.ip(),self._answerer.pid(),self._port))
477 try:
478 try:
479 meta=ServerProxy("http://%s:%d" % (config().get("Metaserver","ip"),config().getint("Metaserver","port")))
480 response=meta.registerServer(self._answerer.ip(),self._answerer.pid(),self._port)
481 self.isRegistered=True
482 foamLogger().info("Registered with server. Response "+str(response))
483 except socket.error, reason:
484 foamLogger().warning("Can't connect to meta-server - SocketError: "+str(reason))
485 except:
486 foamLogger().error("Can't connect to meta-server - Unknown Error: "+str(sys.exc_info()[0]))
487 foamLogger().error(str(sys.exc_info()[1]))
488 foamLogger().error("Traceback: "+str(extract_tb(sys.exc_info()[2])))
489 except:
490
491 pass
492
494 """Tries to deregister with the Meta-Server"""
495
496 if self.isRegistered:
497 try:
498 meta=ServerProxy("http://%s:%d" % (config().get("Metaserver","ip"),config().getint("Metaserver","port")))
499 meta.deregisterServer(self._answerer.ip(),self._answerer.pid(),self._port)
500 except socket.error, reason:
501 foamLogger().warning("Can't connect to meta-server - SocketError: "+str(reason))
502 except:
503 foamLogger().error("Can't connect to meta-server - Unknown Error: "+str(sys.exc_info()[0]))
504 foamLogger().error(str(sys.exc_info()[1]))
505 foamLogger().error("Traceback: "+str(extract_tb(sys.exc_info()[2])))
506 else:
507 foamLogger().warning("Not deregistering, because it seems we were not registered in the first place ")
508 self._server.server_close()
509
511 """Inserts a new line, not to be called via XMLRPC"""
512 self._answerer._insertLine(line)
513