Package PyFoam :: Package Infrastructure :: Module FoamServer
[hide private]
[frames] | no frames]

Source Code for Module PyFoam.Infrastructure.FoamServer

  1  #  ICE Revision: $Id: /local/openfoam/Python/PyFoam/PyFoam/Infrastructure/FoamServer.py 5682 2009-10-01T15:23:03.944623Z bgschaid  $  
  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   
32 -def findFreePort():
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
43 -class FoamAnswerer(object):
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
61 - def _insertLine(self,line):
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
71 - def isFoamServer(self):
72 """This is a Foam-Server (True by default)""" 73 return True
74
75 - def isLiving(self):
76 """The calculation still generates output and therefor seems to be living""" 77 return self.elapsedTime()<self._maxOutputTime
78
79 - def _kill(self):
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
88 - def stop(self):
89 """Stops the run gracefully (after writing the last time-step to disk)""" 90 self._master.stopGracefully() 91 return True
92
93 - def write(self):
94 """Makes the program write the next time-step to disk and the continue""" 95 self._master.writeResults() 96 return True
97
98 - def argv(self):
99 """Argument vector with which the runner was called""" 100 if self._master: 101 return self._master.origArgv 102 else: 103 return []
104
105 - def usedArgv(self):
106 """Argument vector with which the runner started the run""" 107 if self._master: 108 return self._master.argv 109 else: 110 return []
111
112 - def isParallel(self):
113 """Is it a parallel run?""" 114 if self._master: 115 return self._master.lam!=None 116 else: 117 return False
118
119 - def procNr(self):
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
129 - def nrWarnings(self):
130 """Number of warnings the executable emitted""" 131 if self._master: 132 return self._master.warnings 133 else: 134 return 0
135
136 - def commandLine(self):
137 """The command line""" 138 if self._master: 139 return string.join(self._master.origArgv) 140 else: 141 return ""
142
143 - def actualCommandLine(self):
144 """The actual command line used""" 145 if self._master: 146 return self._master.cmd 147 else: 148 return ""
149
150 - def scriptName(self):
151 """Name of the Python-Script that runs the show""" 152 return sys.argv[0]
153
154 - def lastLogLineSeen(self):
155 """@return: the time at which the last log-line was seen""" 156 return self._master.lastLogLineSeen
157
158 - def lastTimeStepSeen(self):
159 """@return: the time at which the last log-line was seen""" 160 return self._master.lastTimeStepSeen
161
162 - def lastLine(self):
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
171 - def tail(self):
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
182 - def elapsedTime(self):
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
190 - def getEnviron(self,name):
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
198 - def mpi(self):
199 """@return: name of the MPI-implementation""" 200 return foamMPI()
201
202 - def foamVersion(self):
203 """Version number of the Foam-Version""" 204 return self.getEnviron("WM_PROJECT_VERSION")
205
206 - def pyFoamVersion(self):
207 """@return: Version number of the PyFoam""" 208 return versionString()
209
210 - def uname(self):
211 """@return: the complete uname-information""" 212 return uname()
213
214 - def ip(self):
215 """@return: the ip of this machine""" 216 try: 217 address = socket.gethostbyname(socket.gethostname()) 218 # This gives 127.0.0.1 if specified so in the /etc/hosts ... 219 except: 220 address = '' 221 if not address or address.startswith('127.'): 222 # ...the hard way. 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 # Got no internet connection 229 address="127.0.0.1" 230 return address
231
232 - def hostname(self):
233 """@return: The name of the computer""" 234 return uname()[1]
235
236 - def configuration(self):
237 """@return: all the configured parameters""" 238 return config().dump()
239
240 - def cwd(self):
241 """@return: the current working directory""" 242 return path.abspath(path.curdir)
243
244 - def pid(self):
245 """@return: the PID of the script""" 246 return getpid()
247
248 - def loadAvg(self):
249 """@return: a tuple with the average loads of the last 1, 5 and 15 minutes""" 250 return getloadavg()
251
252 - def user(self):
253 """@return: the user that runs this script""" 254 return userName()
255
256 - def id(self):
257 """@return: an ID for this run: IP:port:startTimestamp """ 258 return "%s:%d:%f" % (self.ip(),self._foamserver._port,self.startTimestamp())
259
260 - def startTimestamp(self):
261 """@return: the unix-timestamp of the process start""" 262 return self._master.startTimestamp
263
264 - def time(self):
265 """@return: the current time in the simulation""" 266 if self._master.nowTime: 267 return self._master.nowTime 268 else: 269 return 0
270
271 - def createTime(self):
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
278 - def _readParameter(self,name):
279 """Reads a parametr from the controlDict 280 @param name: the parameter 281 @return: The value""" 282 control=ParameterFile(self._master.getSolutionDirectory().controlDict()) 283 return control.readParameter(name)
284
285 - def startTime(self):
286 """@return: parameter startTime from the controlDict""" 287 return float(self._readParameter("startTime"))
288
289 - def endTime(self):
290 """@return: parameter endTime from the controlDict""" 291 return float(self._readParameter("endTime"))
292
293 - def deltaT(self):
294 """@return: parameter startTime from the controlDict""" 295 return float(self._readParameter("deltaT"))
296
297 - def pathToSolution(self):
298 """@return: the path to the solution directory""" 299 return self._master.getSolutionDirectory().name
300
301 - def writtenTimesteps(self):
302 """@return: list of the timesteps on disc""" 303 return self._master.getSolutionDirectory().getTimes()
304
305 - def solutionFiles(self,time):
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
310 - def listFiles(self,directory):
311 """@param directory: Sub-directory of the case 312 @return: List of the filenames (not directories) in that case""" 313 return self._master.getSolutionDirectory().listFiles(directory)
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
346 - def getPlots(self):
347 """Get all the information about the plots""" 348 return allPlots().prepareForTransfer()
349
350 - def getPlotData(self):
351 """Get all the data for the plots""" 352 return allLines().prepareForTransfer()
353
354 - def controlDictUnmodified(self):
355 """Checks whether there is a pending change to the controlDict""" 356 return self._master.controlDict == None
357
358 - def getRemark(self):
359 """Get the user-defined remark for this job""" 360 if self._master.remark: 361 return self._master.remark 362 else: 363 return ""
364
365 - def setRemark(self,remark):
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
372 - def jobId(self):
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
379 -class FoamServer(Thread):
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
435 - def run(self):
436 foamLogger().info("Running server at port %d" % self._port) 437 if self._port<0: 438 return 439 # wait befor registering to avoid timeouts 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 # This seems to be necessary since python 2.6 450 pass 451 452 # self._server.serve_forever() # the old way 453 self._server.server_close() 454 455 foamLogger().warning("Stopped serving on port %d" % self._port)
456
457 - def info(self):
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
462 - def kill(self):
463 """Interrupts the FOAM-process (and kills the server)""" 464 self._answerer._kill() 465 return self.killServer()
466
467 - def killServer(self):
468 """Kills the server process""" 469 tmp=self._running 470 self._running=False 471 return tmp
472
473 - def register(self):
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 # print "Error during registering (no socket module?)" 491 pass
492
493 - def deregister(self):
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
510 - def _insertLine(self,line):
511 """Inserts a new line, not to be called via XMLRPC""" 512 self._answerer._insertLine(line)
513