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 7641 2011-12-18T21:49:13.536132Z 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 runnerData(self):
155 """@return: the data the runner collected so far""" 156 return self._master.data
157
158 - def lastLogLineSeen(self):
159 """@return: the time at which the last log-line was seen""" 160 return self._master.lastLogLineSeen
161
162 - def lastTimeStepSeen(self):
163 """@return: the time at which the last log-line was seen""" 164 return self._master.lastTimeStepSeen
165
166 - def lastLine(self):
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
175 - def tail(self):
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
186 - def elapsedTime(self):
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
194 - def getEnviron(self,name):
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
202 - def mpi(self):
203 """@return: name of the MPI-implementation""" 204 return foamMPI()
205
206 - def foamVersion(self):
207 """Version number of the Foam-Version""" 208 return self.getEnviron("WM_PROJECT_VERSION")
209
210 - def pyFoamVersion(self):
211 """@return: Version number of the PyFoam""" 212 return versionString()
213
214 - def uname(self):
215 """@return: the complete uname-information""" 216 return uname()
217
218 - def ip(self):
219 """@return: the ip of this machine""" 220 try: 221 address = socket.gethostbyname(socket.gethostname()) 222 # This gives 127.0.0.1 if specified so in the /etc/hosts ... 223 except: 224 address = '' 225 if not address or address.startswith('127.'): 226 # ...the hard way. 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 # Got no internet connection 233 address="127.0.0.1" 234 return address
235
236 - def hostname(self):
237 """@return: The name of the computer""" 238 return uname()[1]
239
240 - def configuration(self):
241 """@return: all the configured parameters""" 242 return config().dump()
243
244 - def cwd(self):
245 """@return: the current working directory""" 246 return path.abspath(path.curdir)
247
248 - def pid(self):
249 """@return: the PID of the script""" 250 return getpid()
251
252 - def loadAvg(self):
253 """@return: a tuple with the average loads of the last 1, 5 and 15 minutes""" 254 return getloadavg()
255
256 - def user(self):
257 """@return: the user that runs this script""" 258 return userName()
259
260 - def id(self):
261 """@return: an ID for this run: IP:port:startTimestamp """ 262 return "%s:%d:%f" % (self.ip(),self._foamserver._port,self.startTimestamp())
263
264 - def startTimestamp(self):
265 """@return: the unix-timestamp of the process start""" 266 return self._master.startTimestamp
267
268 - def time(self):
269 """@return: the current time in the simulation""" 270 if self._master.nowTime: 271 return self._master.nowTime 272 else: 273 return 0
274
275 - def createTime(self):
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
282 - def _readParameter(self,name):
283 """Reads a parametr from the controlDict 284 @param name: the parameter 285 @return: The value""" 286 control=ParameterFile(self._master.getSolutionDirectory().controlDict()) 287 return control.readParameter(name)
288
289 - def startTime(self):
290 """@return: parameter startTime from the controlDict""" 291 return float(self._readParameter("startTime"))
292
293 - def endTime(self):
294 """@return: parameter endTime from the controlDict""" 295 return float(self._readParameter("endTime"))
296
297 - def deltaT(self):
298 """@return: parameter startTime from the controlDict""" 299 return float(self._readParameter("deltaT"))
300
301 - def pathToSolution(self):
302 """@return: the path to the solution directory""" 303 return self._master.getSolutionDirectory().name
304
305 - def writtenTimesteps(self):
306 """@return: list of the timesteps on disc""" 307 return self._master.getSolutionDirectory().getTimes()
308
309 - def solutionFiles(self,time):
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
314 - def listFiles(self,directory):
315 """@param directory: Sub-directory of the case 316 @return: List of the filenames (not directories) in that case""" 317 return self._master.getSolutionDirectory().listFiles(directory)
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
350 - def getPlots(self):
351 """Get all the information about the plots""" 352 return allPlots().prepareForTransfer()
353
354 - def getPlotData(self):
355 """Get all the data for the plots""" 356 return allLines().prepareForTransfer()
357
358 - def controlDictUnmodified(self):
359 """Checks whether there is a pending change to the controlDict""" 360 return self._master.controlDict == None
361
362 - def getRemark(self):
363 """Get the user-defined remark for this job""" 364 if self._master.remark: 365 return self._master.remark 366 else: 367 return ""
368
369 - def setRemark(self,remark):
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
376 - def jobId(self):
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
383 -class FoamServer(Thread):
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
439 - def run(self):
440 foamLogger().info("Running server at port %d" % self._port) 441 if self._port<0: 442 return 443 # wait befor registering to avoid timeouts 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 # This seems to be necessary since python 2.6 454 pass 455 456 # self._server.serve_forever() # the old way 457 self._server.server_close() 458 459 foamLogger().warning("Stopped serving on port %d" % self._port)
460
461 - def info(self):
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
466 - def kill(self):
467 """Interrupts the FOAM-process (and kills the server)""" 468 self._answerer._kill() 469 return self.killServer()
470
471 - def killServer(self):
472 """Kills the server process""" 473 tmp=self._running 474 self._running=False 475 return tmp
476
477 - def register(self):
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 # print "Error during registering (no socket module?)" 495 pass
496
497 - def deregister(self):
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
514 - def _insertLine(self,line):
515 """Inserts a new line, not to be called via XMLRPC""" 516 self._answerer._insertLine(line)
517