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 data the runner collected so far"""
156 return self._master.data
157
159 """@return: the time at which the last log-line was seen"""
160 return self._master.lastLogLineSeen
161
163 """@return: the time at which the last log-line was seen"""
164 return self._master.lastTimeStepSeen
165
167 """@return: the last line that was output by the running FOAM-process"""
168 self._linesLock.acquire()
169 result=self._lines.last()
170 self._linesLock.release()
171 if not result:
172 return ""
173 return result
174
176 """@return: the current last lines as a string"""
177 self._linesLock.acquire()
178 tmp=self._lines.dump()
179 self._linesLock.release()
180 result=""
181 for l in tmp:
182 result+=l
183
184 return result
185
187 """@return: time in seconds since the last line was output"""
188 self._linesLock.acquire()
189 result=time()-self._lastTime
190 self._linesLock.release()
191
192 return result
193
195 """@param name: name of an environment variable
196 @return: value of the variable, empty string if non-existing"""
197 result=""
198 if environ.has_key(name):
199 result=environ[name]
200 return result
201
203 """@return: name of the MPI-implementation"""
204 return foamMPI()
205
207 """Version number of the Foam-Version"""
208 return self.getEnviron("WM_PROJECT_VERSION")
209
211 """@return: Version number of the PyFoam"""
212 return versionString()
213
215 """@return: the complete uname-information"""
216 return uname()
217
219 """@return: the ip of this machine"""
220 try:
221 address = socket.gethostbyname(socket.gethostname())
222
223 except:
224 address = ''
225 if not address or address.startswith('127.'):
226
227 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
228 try:
229 s.connect(('4.2.2.1', 0))
230 address = s.getsockname()[0]
231 except:
232
233 address="127.0.0.1"
234 return address
235
237 """@return: The name of the computer"""
238 return uname()[1]
239
241 """@return: all the configured parameters"""
242 return config().dump()
243
245 """@return: the current working directory"""
246 return path.abspath(path.curdir)
247
249 """@return: the PID of the script"""
250 return getpid()
251
253 """@return: a tuple with the average loads of the last 1, 5 and 15 minutes"""
254 return getloadavg()
255
257 """@return: the user that runs this script"""
258 return userName()
259
261 """@return: an ID for this run: IP:port:startTimestamp """
262 return "%s:%d:%f" % (self.ip(),self._foamserver._port,self.startTimestamp())
263
265 """@return: the unix-timestamp of the process start"""
266 return self._master.startTimestamp
267
269 """@return: the current time in the simulation"""
270 if self._master.nowTime:
271 return self._master.nowTime
272 else:
273 return 0
274
276 """@return: the time in the simulation for which the mesh was created"""
277 if self._master.nowTime:
278 return self._master.createTime
279 else:
280 return 0
281
288
290 """@return: parameter startTime from the controlDict"""
291 return float(self._readParameter("startTime"))
292
294 """@return: parameter endTime from the controlDict"""
295 return float(self._readParameter("endTime"))
296
298 """@return: parameter startTime from the controlDict"""
299 return float(self._readParameter("deltaT"))
300
304
308
310 """@param time: name of the timestep
311 @return: list of the solution files at that timestep"""
312 return self._master.getSolutionDirectory()[time].getFiles()
313
318
319 - def getDictionaryText(self,directory,name):
320 """@param directory: Sub-directory of the case
321 @param name: name of the dictionary file
322 @return: the contents of the file as a big string"""
323 return self._master.getSolutionDirectory().getDictionaryText(directory,name)
324
325 - def getDictionaryContents(self,directory,name):
326 """@param directory: Sub-directory of the case
327 @param name: name of the dictionary file
328 @return: the contents of the file as a python data-structure"""
329 return self._master.getSolutionDirectory().getDictionaryContents(directory,name)
330
331 - def writeDictionaryText(self,directory,name,text):
332 """Writes the contents of a dictionary
333 @param directory: Sub-directory of the case
334 @param name: name of the dictionary file
335 @param text: String with the dictionary contents"""
336
337 self._master.getSolutionDirectory().writeDictionaryText(directory,name,text)
338
339 return True
340
341 - def writeDictionaryContents(self,directory,name,contents):
342 """Writes the contents of a dictionary
343 @param directory: Sub-directory of the case
344 @param name: name of the dictionary file
345 @param contents: Python-dictionary with the dictionary contents"""
346
347 self._master.getSolutionDirectory().writeDictionaryContents(directory,name,contents)
348 return True
349
353
357
359 """Checks whether there is a pending change to the controlDict"""
360 return self._master.controlDict == None
361
363 """Get the user-defined remark for this job"""
364 if self._master.remark:
365 return self._master.remark
366 else:
367 return ""
368
370 """Overwrite the user-defined remark
371 @return: True if the remark was set previously"""
372 oldRemark=self._master.remark
373 self._master.remark=remark
374 return oldRemark!=None
375
377 """Return the job-ID of the queuing-system. Empty if unset"""
378 if self._master.jobId:
379 return self._master.jobId
380 else:
381 return ""
382
384 """This is the class that serves the requests about the FOAM-Run"""
385 - def __init__(self,run=None,master=None,lines=100):
386 """
387 @param run: The thread that controls the run
388 @param master: The Runner-Object that controls everything
389 @param lines: the number of lines the server should remember
390 """
391 Thread.__init__(self)
392
393 self.isRegistered=False
394
395 tries=0
396 maxTries=length=config().getint("Network","socketRetries")
397
398 ok=False
399
400 while not ok and tries<maxTries:
401 ok=True
402 tries+=1
403
404 self._port=findFreePort()
405
406 self._running=False
407
408 if self._port<0:
409 foamLogger().warning("Could not get a free port. Server not started")
410 return
411
412 try:
413 foamLogger().info("Serving on port %d" % self._port)
414 self._server=ServerBase(('',self._port),logRequests=False)
415 self._server.register_introspection_functions()
416 self._answerer=FoamAnswerer(run=run,master=master,lines=lines,foamserver=self)
417 self._server.register_instance(self._answerer)
418 self._server.register_function(self.killServer)
419 self._server.register_function(self.kill)
420 if run:
421 self._server.register_function(run.cpuTime)
422 self._server.register_function(run.cpuUserTime)
423 self._server.register_function(run.cpuSystemTime)
424 self._server.register_function(run.wallTime)
425 self._server.register_function(run.usedMemory)
426 except socket.error,reason:
427 ok=False
428 warning("Could not start on port",self._port,"althoug it was promised. Try:",tries,"of",maxTries)
429 foamLogger().warning("Could not get port %d - SocketError: %s. Try %d of %d" % (self._port,str(reason),tries,maxTries))
430 sleep(2+20*random())
431
432 if not ok:
433 foamLogger().warning("Exceeded maximum number of tries for getting a port: %d" % maxTries)
434 warning("Did not get a port after %d tries" % tries)
435 else:
436 if tries>1:
437 warning("Got a port after %d tries" % tries)
438
440 foamLogger().info("Running server at port %d" % self._port)
441 if self._port<0:
442 return
443
444 reg=Timer(5.,self.register)
445 reg.start()
446
447 self._running=True
448
449 try:
450 while self._running:
451 self._server.handle_request()
452 except select.error,e:
453
454 pass
455
456
457 self._server.server_close()
458
459 foamLogger().warning("Stopped serving on port %d" % self._port)
460
462 """Returns the IP, the PID and the port of the server (as one tuple)"""
463
464 return self._answerer.ip(),self._answerer.pid(),self._port
465
467 """Interrupts the FOAM-process (and kills the server)"""
468 self._answerer._kill()
469 return self.killServer()
470
472 """Kills the server process"""
473 tmp=self._running
474 self._running=False
475 return tmp
476
478 """Tries to register with the Meta-Server"""
479
480 foamLogger().info("Trying to register as IP:%s PID:%d Port:%d" % (self._answerer.ip(),self._answerer.pid(),self._port))
481 try:
482 try:
483 meta=ServerProxy("http://%s:%d" % (config().get("Metaserver","ip"),config().getint("Metaserver","port")))
484 response=meta.registerServer(self._answerer.ip(),self._answerer.pid(),self._port)
485 self.isRegistered=True
486 foamLogger().info("Registered with server. Response "+str(response))
487 except socket.error, reason:
488 foamLogger().warning("Can't connect to meta-server - SocketError: "+str(reason))
489 except:
490 foamLogger().error("Can't connect to meta-server - Unknown Error: "+str(sys.exc_info()[0]))
491 foamLogger().error(str(sys.exc_info()[1]))
492 foamLogger().error("Traceback: "+str(extract_tb(sys.exc_info()[2])))
493 except:
494
495 pass
496
498 """Tries to deregister with the Meta-Server"""
499
500 if self.isRegistered:
501 try:
502 meta=ServerProxy("http://%s:%d" % (config().get("Metaserver","ip"),config().getint("Metaserver","port")))
503 meta.deregisterServer(self._answerer.ip(),self._answerer.pid(),self._port)
504 except socket.error, reason:
505 foamLogger().warning("Can't connect to meta-server - SocketError: "+str(reason))
506 except:
507 foamLogger().error("Can't connect to meta-server - Unknown Error: "+str(sys.exc_info()[0]))
508 foamLogger().error(str(sys.exc_info()[1]))
509 foamLogger().error("Traceback: "+str(extract_tb(sys.exc_info()[2])))
510 else:
511 foamLogger().warning("Not deregistering, because it seems we were not registered in the first place ")
512 self._server.server_close()
513
515 """Inserts a new line, not to be called via XMLRPC"""
516 self._answerer._insertLine(line)
517