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

Source Code for Module PyFoam.Infrastructure.FoamServer

  1  #  ICE Revision: $Id: FoamServer.py 9676 2008-11-13 12:25:43Z 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   
 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   
29 -def findFreePort():
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
40 -class FoamAnswerer(object):
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
57 - def _insertLine(self,line):
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
67 - def isFoamServer(self):
68 """This is a Foam-Server (True by default)""" 69 return True
70
71 - def isLiving(self):
72 """The calculation still generates output and therefor seems to be living""" 73 return self.elapsedTime()<self._maxOutputTime
74
75 - def _kill(self):
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
84 - def stop(self):
85 """Stops the run gracefully (after writing the last time-step to disk)""" 86 self._master.stopGracefully() 87 return True
88
89 - def write(self):
90 """Makes the program write the next time-step to disk and the continue""" 91 self._master.writeResults() 92 return True
93
94 - def argv(self):
95 """Argument vector with which the runner was called""" 96 if self._master: 97 return self._master.origArgv 98 else: 99 return []
100
101 - def usedArgv(self):
102 """Argument vector with which the runner started the run""" 103 if self._master: 104 return self._master.argv 105 else: 106 return []
107
108 - def isParallel(self):
109 """Is it a parallel run?""" 110 if self._master: 111 return self._master.lam!=None 112 else: 113 return False
114
115 - def procNr(self):
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
125 - def nrWarnings(self):
126 """Number of warnings the executable emitted""" 127 if self._master: 128 return self._master.warnings 129 else: 130 return 0
131
132 - def commandLine(self):
133 """The command line""" 134 if self._master: 135 return string.join(self._master.origArgv) 136 else: 137 return ""
138
139 - def actualCommandLine(self):
140 """The actual command line used""" 141 if self._master: 142 return self._master.cmd 143 else: 144 return ""
145
146 - def scriptName(self):
147 """Name of the Python-Script that runs the show""" 148 return sys.argv[0]
149
150 - def lastLine(self):
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
159 - def tail(self):
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
170 - def elapsedTime(self):
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
178 - def getEnviron(self,name):
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
186 - def mpi(self):
187 """@return: name of the MPI-implementation""" 188 return foamMPI()
189
190 - def foamVersion(self):
191 """Version number of the Foam-Version""" 192 return self.getEnviron("WM_PROJECT_VERSION")
193
194 - def pyFoamVersion(self):
195 """@return: Version number of the PyFoam""" 196 return versionString()
197
198 - def uname(self):
199 """@return: the complete uname-information""" 200 return uname()
201
202 - def ip(self):
203 """@return: the ip of this machine""" 204 return socket.gethostbyname(socket.gethostname())
205
206 - def hostname(self):
207 """@return: The name of the computer""" 208 return uname()[1]
209
210 - def configuration(self):
211 """@return: all the configured parameters""" 212 return config().dump()
213
214 - def cwd(self):
215 """@return: the current working directory""" 216 return path.abspath(path.curdir)
217
218 - def pid(self):
219 """@return: the PID of the script""" 220 return getpid()
221
222 - def user(self):
223 """@return: the user that runs this script""" 224 return userName()
225
226 - def id(self):
227 """@return: an ID for this run: IP and process-id""" 228 return "%s:%d" % (self.ip(),self.pid())
229
230 - def time(self):
231 """@return: the current time in the simulation""" 232 if self._master.nowTime: 233 return self._master.nowTime 234 else: 235 return 0
236
237 - def createTime(self):
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
244 - def _readParameter(self,name):
245 """Reads a parametr from the controlDict 246 @param name: the parameter 247 @return: The value""" 248 control=ParameterFile(self._master.getSolutionDirectory().controlDict()) 249 return control.readParameter(name)
250
251 - def startTime(self):
252 """@return: parameter startTime from the controlDict""" 253 return float(self._readParameter("startTime"))
254
255 - def endTime(self):
256 """@return: parameter endTime from the controlDict""" 257 return float(self._readParameter("endTime"))
258
259 - def deltaT(self):
260 """@return: parameter startTime from the controlDict""" 261 return float(self._readParameter("deltaT"))
262
263 -class FoamServer(Thread):
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
317 - def run(self):
318 if self._port<0: 319 return 320 # wait befor registering to avoid timeouts 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 # self._server.serve_forever() # the old way 330 self._server.server_close() 331 332 foamLogger().info("Stopped serving on port %d" % self._port)
333
334 - def info(self):
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
339 - def kill(self):
340 """Interrupts the FOAM-process (and kills the server)""" 341 self._answerer._kill() 342 return self.killServer()
343
344 - def killServer(self):
345 """Kills the server process""" 346 tmp=self._running 347 self._running=False 348 return tmp
349
350 - def register(self):
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 # print "Error during registering (no socket module?)" 365 pass
366
367 - def deregister(self):
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
381 - def _insertLine(self,line):
382 """Inserts a new line, not to be called via XMLRPC""" 383 self._answerer._insertLine(line)
384