gyorgy@0: """ gyorgy@0: ServerWrapper.py gyorgy@0: gyorgy@0: Created by George Fazekas, QMUL on 2009-05-09. gyorgy@0: Copyright (c) 2009 QMUL. All rights reserved. gyorgy@0: """ gyorgy@0: from __future__ import with_statement gyorgy@0: import os,sys gyorgy@0: import cherrypy gyorgy@0: from cherrypy.lib import static gyorgy@0: import subprocess as sp gyorgy@0: from subprocess import Popen as spopen gyorgy@0: gyorgy@0: try : gyorgy@0: from cherrypy.lib import safemime gyorgy@0: except: gyorgy@0: print "Warning: import safemime failed. (Probably newer cherrypy version.)" gyorgy@0: #safemime.init() gyorgy@0: #root._cp_config = {'tools.safe_multipart.on': True} gyorgy@0: gyorgy@0: __localdir__ = os.path.realpath(os.path.dirname(os.path.abspath(__file__))) gyorgy@0: gyorgy@0: # ============================================ gyorgy@0: # Shared Configuration data container gyorgy@0: # ============================================ gyorgy@0: gyorgy@0: class Config(object): gyorgy@0: '''A shared config object''' gyorgy@0: gyorgy@0: def __init__(self,root=None,local=False,conf='not set'): gyorgy@0: self.cherrypyVersion = 0 gyorgy@0: self.nice_value = 0 gyorgy@0: self.svn_revision = 0 gyorgy@0: self.hostname = str() gyorgy@0: self.localdir = __localdir__ gyorgy@0: self.server_config_file = str() gyorgy@0: self.local = local gyorgy@0: self.server_config_file = conf gyorgy@0: self.root = root gyorgy@0: gyorgy@0: gyorgy@0: # ============================================ gyorgy@0: # Cherrypy ServerWrapper gyorgy@0: # ============================================ gyorgy@0: gyorgy@0: # This is a thin wrapper on cherrypy gyorgy@0: class ServerWrapper(object): gyorgy@0: gyorgy@0: def __init__(self,config): gyorgy@0: self.conf = config gyorgy@0: self.config = self.conf gyorgy@0: gyorgy@0: gyorgy@0: def quickstart(self,ip=None,retries=5): gyorgy@0: '''Start fx-test''' gyorgy@0: if not retries : gyorgy@0: print 'Server: Error: Retry limit reached while trying to start service.' gyorgy@0: # TDOD: maybe we want to shut the daemon here as well... to avoid external loop gyorgy@0: return False gyorgy@0: self.config.cherrypyVersion = cherrypy.__version__ gyorgy@0: print 'Server: Cherrypy version: %s' %str(cherrypy.__version__) gyorgy@0: gyorgy@0: cherrypy.server.socket_timeout = 60 gyorgy@0: cherrypy.config.update({'tools.staticdir.root': __localdir__}) gyorgy@0: # cherrypy.config.update('./config/server.cfg') gyorgy@0: cherrypy.config.update(self.config.server_config_file) gyorgy@0: if type(ip) == str and ip.count('.') == 3: gyorgy@0: _ip = ip.split(':')[0] gyorgy@0: if ip.count(':') and len(ip.split(':')[-1]) == 4: gyorgy@0: _port = int(ip.split(':')[-1]) gyorgy@0: cherrypy.config.update({'server.socket_port': _port}) gyorgy@0: else : gyorgy@0: _port = int(cherrypy.server.socket_port) gyorgy@0: print "Server: No valid port specified for service. Using default: %i" %_port gyorgy@0: cherrypy.config.update({'server.socket_host': _ip}) gyorgy@0: else : gyorgy@0: _ip = str(cherrypy.server.socket_host) gyorgy@0: _port = int(cherrypy.server.socket_port) gyorgy@0: gyorgy@0: # pass in the global config object to the session: gyorgy@0: cherrypy.config.update({'tools.sessions.GLOBAL_CONFIG': self.config}) gyorgy@0: gyorgy@0: #enable safemime tool to avoid upload timeouts gyorgy@0: try : gyorgy@0: safemime.init() gyorgy@0: cherrypy.config.update({'tools.safe_multipart.on': True}) gyorgy@0: except: gyorgy@0: pass gyorgy@0: gyorgy@0: # set hostname in config gyorgy@0: if not self.config.hostname : gyorgy@0: self.config.hostname = str(cherrypy.server.socket_host).strip()+":"+str(cherrypy.server.socket_port).strip() gyorgy@0: print 'Server: hostname is set to: ',self.config.hostname gyorgy@0: gyorgy@0: # report process pid and niceness gyorgy@0: print "Server: pid: ", os.getpid() gyorgy@0: # self.setProcessPriority(-3,'passwd') gyorgy@0: self.config.nice_value = self.getProcessPriority() gyorgy@0: print "Server: nice value: ", self.config.nice_value gyorgy@0: gyorgy@0: # report subversion rev. number gyorgy@0: self.config.svn_revision = self.getRevisionNumber() gyorgy@0: print "Server: SVN revision: ", self.config.svn_revision gyorgy@0: gyorgy@0: try : gyorgy@0: gyorgy@0: print 'Server: quickstarting apps at >> %s << (bounded ip/port: %s:%i)\n' \ gyorgy@0: %(__localdir__,_ip,_port) gyorgy@0: # this will block gyorgy@0: if self.conf.root : gyorgy@0: # cherrypy.quickstart(self.conf.root,script_name='',config=self.conf.server_config_file) gyorgy@0: cherrypy.quickstart(self.conf.root, config=self.conf.server_config_file) gyorgy@0: else : gyorgy@0: # cherrypy.quickstart(script_name='',config=self.conf.server_config_file) gyorgy@0: cherrypy.quickstart(config=self.conf.server_config_file) gyorgy@0: gyorgy@0: gyorgy@0: except IOError, e: gyorgy@0: print "\n\n>>>Server::quickstart: IOError: Port not free: ",_ip,":",_port,"\nException: ",str(e) gyorgy@0: if self.getProcessPids(_port,kill=True) : gyorgy@0: self.quickstart(ip,retries-1) gyorgy@0: gyorgy@0: except cherrypy.server.interrupt, e: gyorgy@0: print "\n\n>>>Server::quickstart: Server interrupt: Port may be used: ",_ip,":",_port,"\nException: ",str(e) gyorgy@0: if self.getProcessPids(_port,kill=True) : gyorgy@0: self.quickstart(ip,retries-1) gyorgy@0: gyorgy@0: except BaseException, e: gyorgy@0: print "\n\n>>>Server::quickstart: Exception caught while starting the service." gyorgy@0: if 'Address already in use' in e : gyorgy@0: print "Port already in use: ",_ip,":",_port gyorgy@0: if self.getProcessPids(_port,kill=True) : gyorgy@0: self.quickstart(ip,retries-1) gyorgy@0: else: gyorgy@0: print "Port may be in use: ",_ip,":",_port,"\nException: ",str(e) gyorgy@0: raise gyorgy@0: gyorgy@0: except : gyorgy@0: print "\n\n>>>Server::quickstart: Unknown exception caught while starting the service." gyorgy@0: print "Port may be in use: ",_ip,":",_port gyorgy@0: raise gyorgy@0: # cherrypy.quickstart(script_name='', config='config/server.cfg') gyorgy@0: gyorgy@0: gyorgy@0: def mount(self,app,path_name=''): gyorgy@0: # cherrypy.tree.mount(requesthandler,config=appconfig) gyorgy@0: cherrypy.tree.mount(app,script_name=path_name,config=self.config.server_config_file) gyorgy@0: gyorgy@0: gyorgy@0: def getProcessPids(self,port,kill=False): gyorgy@0: '''Get the pid of the offending Python process given a port after an unsuccessful restart.''' gyorgy@0: print "Running lsof -i :"+str(port)," ...\n\n" gyorgy@0: command = "lsof -i :"+str(port) gyorgy@0: w = spopen(command,stdout=sp.PIPE,stderr=sp.PIPE,shell=True) gyorgy@0: se = w.stderr.readlines() gyorgy@0: result = w.stdout.readlines() gyorgy@0: exitcode = w.wait() gyorgy@0: if not result : gyorgy@0: print "getProcessPid:: Unable to obtain process pid. (lsof returned nothing. exitcode: %s)" %str(exitcode) gyorgy@0: return False gyorgy@0: import pprint gyorgy@0: pprint.pprint(result) gyorgy@0: gyorgy@0: # get heading: gyorgy@0: ix = None gyorgy@0: head = result[0].upper() gyorgy@0: if 'PID' in head: gyorgy@0: head = filter(lambda x: x != str(), head.split(' ')) gyorgy@0: head = map(lambda x: x.strip().replace(' ',''), head) gyorgy@0: if 'PID' in head : ix = head.index('PID') gyorgy@0: # get process pid gyorgy@0: pids = [] gyorgy@0: for line in result : gyorgy@0: if 'python' in line.lower() : gyorgy@0: line = filter(lambda x: x != str(), line.split(' ')) gyorgy@0: line = map(lambda x: x.strip().replace(' ',''), line) gyorgy@0: try : gyorgy@0: if ix : gyorgy@0: pids.append(int(line[ix])) gyorgy@0: else: gyorgy@0: numbers = filter(lambda x: x.isdigit(), line) gyorgy@0: pids.append(int(numbers[0])) gyorgy@0: except: gyorgy@0: print 'Error parsing lsof results.' gyorgy@0: return False gyorgy@0: print 'Pids found: ',pids gyorgy@0: # kill if specified gyorgy@0: if kill : gyorgy@0: pids_killed = [] gyorgy@0: import signal gyorgy@0: for pid in pids: gyorgy@0: print 'Killing process: ',pid gyorgy@0: try : gyorgy@0: os.kill(pid,signal.SIGKILL) gyorgy@0: pids_killed.append(pid) gyorgy@0: except : gyorgy@0: print 'Failed: ',pid gyorgy@0: if pids_killed : gyorgy@0: print 'Processes killed:',pids_killed,' Waiting 10 secods...' gyorgy@0: import time gyorgy@0: time.sleep(10) gyorgy@0: return True gyorgy@0: return False gyorgy@0: gyorgy@0: gyorgy@0: def getProcessPriority(self): gyorgy@0: '''Get the priority of this Python process.''' gyorgy@0: pid = os.getpid() gyorgy@0: # print "Running ps -p %s -o nice" %str(pid)," ...\n\n" gyorgy@0: command = "ps -p %s -o nice" %str(pid) gyorgy@0: w = spopen(command,stdout=sp.PIPE,stderr=sp.PIPE,shell=True) gyorgy@0: se = w.stderr.readlines() gyorgy@0: result = w.stdout.readlines() gyorgy@0: exitcode = w.wait() gyorgy@0: if not result : gyorgy@0: print "getProcessPriority:: Unable to obtain process proirity. (ps returned nothing. exitcode: %s)" %str(exitcode) gyorgy@0: return None gyorgy@0: try : gyorgy@0: return int(result[1].strip()) gyorgy@0: except: gyorgy@0: print "getProcessPriority:: Error parsing process proirity value. (ps returned: \n%s, \nexitcode: %s)" %(str(result),str(exitcode)) gyorgy@0: return None gyorgy@0: gyorgy@0: gyorgy@0: def setProcessPriority(self,value,passwd): gyorgy@0: '''Set the priority of this Python process.''' gyorgy@0: pid = os.getpid() gyorgy@0: command = "sudo -S renice -n %s -p %s" %(str(value),str(pid)) gyorgy@0: w = spopen(command,stdout=sp.PIPE,stdin=sp.PIPE,stderr=sp.PIPE,shell=True) gyorgy@0: result,se = w.communicate(passwd) gyorgy@0: gyorgy@0: gyorgy@0: def getRevisionNumber(self): gyorgy@0: '''Get the Version control revision number by running svn info''' gyorgy@0: w = spopen("svn info",stdout=sp.PIPE,stdin=None,stderr=sp.PIPE,shell=True) gyorgy@0: try : gyorgy@0: result,err = w.communicate(None) gyorgy@0: rev = 0 gyorgy@0: for line in result.split('\n'): gyorgy@0: if line.startswith('Revision') : gyorgy@0: rev = int(line.split(':')[1].strip()) gyorgy@0: return rev gyorgy@0: except: gyorgy@0: return None gyorgy@0: gyorgy@0: gyorgy@0: gyorgy@0: def getHitCount(self): gyorgy@0: if hasattr(cherrypy,'session') : gyorgy@0: count = cherrypy.session.get('count', 0) + 1 gyorgy@0: cherrypy.session['count'] = count gyorgy@0: return count gyorgy@0: else: gyorgy@0: return 0 gyorgy@0: gyorgy@0: def getSessionID(self): gyorgy@0: if hasattr(cherrypy,'session') : gyorgy@0: self.getHitCount() gyorgy@0: return str(cherrypy.session.id) gyorgy@0: else: gyorgy@0: raise RuntimeError('server::getSessionID: No session information available.') gyorgy@0: gyorgy@0: def getRequestSessionID(self): gyorgy@0: cookie = cherrypy.request.cookie gyorgy@0: if cookie.has_key('session_id') : gyorgy@0: cookie = cookie['session_id'].output(header='') gyorgy@0: rsid = cookie.split('session_id=')[-1] gyorgy@0: return str(rsid) gyorgy@0: else : gyorgy@0: return None gyorgy@0: gyorgy@0: def validateSession(self): gyorgy@0: if hasattr(cherrypy,'session') \ gyorgy@0: and self.getSessionID() == self.getRequestSessionID() : gyorgy@0: return True gyorgy@0: else: gyorgy@0: if self.getRequestSessionID() is None : gyorgy@0: cookie = cherrypy.request.cookie gyorgy@0: log_msg = "SessionID mismatch: " + str(self.getSessionID()) + \ gyorgy@0: " Received: None, Coockie was: " + str(cookie) gyorgy@0: cherrypy.log(log_msg) gyorgy@0: return False gyorgy@0: gyorgy@0: def adminSession(self): gyorgy@0: if hasattr(cherrypy,'session') and cherrypy.session.has_key('auth') and cherrypy.session['auth'] : gyorgy@0: return True gyorgy@0: return False gyorgy@0: gyorgy@0: def getFullPath(self,filelist): gyorgy@0: if (type(filelist) == list) : gyorgy@0: return map(lambda x: self.getFullPath(x),filelist) gyorgy@0: else: gyorgy@0: if type(filelist) != str and type(filelist) != unicode : gyorgy@0: e ='server::getFullPath: Wrong path name in file list: %s' %(str(type(filelist))) gyorgy@0: raise RuntimeError(e) gyorgy@0: return os.path.join(self.getSessionPath(),filelist) gyorgy@0: gyorgy@0: def getSessionPath(self): gyorgy@0: if hasattr(cherrypy,'session') : gyorgy@0: return cherrypy.session.get_session_path() gyorgy@0: else: gyorgy@0: raise RuntimeError('server::getSessionPath: No session information available.') gyorgy@0: gyorgy@0: def getSessionCount(self) : gyorgy@0: if hasattr(cherrypy,'session') : gyorgy@0: return len(cherrypy.session) gyorgy@0: else: gyorgy@0: raise RuntimeError('server::getSessionPath: No session information available.') gyorgy@0: gyorgy@0: def getActiveSessions(self) : gyorgy@0: if hasattr(cherrypy,'session') : gyorgy@0: return cherrypy.session.cache.keys() gyorgy@0: else: gyorgy@0: raise RuntimeError('server::getSessionPath: No session information available.') gyorgy@0: gyorgy@0: