view ServerWrapper.py @ 25:4a4bd554b4c1 tip

Closing this sub branch.
author Daniele Barchiesi <daniele.barchiesi@eecs.qmul.ac.uk>
date Mon, 25 Mar 2013 14:02:54 +0000
parents 032bc65ebafc
children
line wrap: on
line source
"""
ServerWrapper.py

Created by  George Fazekas, QMUL on 2009-05-09.
Copyright (c) 2009 QMUL. All rights reserved.
"""
from __future__ import with_statement
import os,sys
import cherrypy
from cherrypy.lib import static
import subprocess as sp
from subprocess import Popen as spopen

try :
	from cherrypy.lib import safemime
except:
	print "Warning: import safemime failed. (Probably newer cherrypy version.)"
#safemime.init()
#root._cp_config = {'tools.safe_multipart.on': True}

__localdir__ = os.path.realpath(os.path.dirname(os.path.abspath(__file__)))

# ============================================
# Shared Configuration data container
# ============================================

class Config(object):
	'''A shared config object'''
	
	def __init__(self,root=None,local=False,conf='not set'):
		self.cherrypyVersion = 0
		self.nice_value = 0
		self.svn_revision = 0
		self.hostname = str()
		self.localdir = __localdir__
		self.server_config_file = str()
		self.local = local
		self.server_config_file = conf
		self.root = root


# ============================================
# Cherrypy ServerWrapper
# ============================================

# This is a thin wrapper on cherrypy
class ServerWrapper(object):
	
	def __init__(self,config):
		self.conf = config
		self.config = self.conf
		
	
	def quickstart(self,ip=None,retries=5):
		'''Start fx-test'''
		if not retries : 
			print 'Server: Error: Retry limit reached while trying to start service.'
			# TDOD: maybe we want to shut the daemon here as well... to avoid external loop
			return False
		self.config.cherrypyVersion = cherrypy.__version__
		print 'Server: Cherrypy version: %s' %str(cherrypy.__version__)

		cherrypy.server.socket_timeout = 60
		cherrypy.config.update({'tools.staticdir.root': __localdir__})
		# cherrypy.config.update('./config/server.cfg')
		cherrypy.config.update(self.config.server_config_file)
		if type(ip) == str and ip.count('.') == 3:
			_ip = ip.split(':')[0]
			if ip.count(':') and len(ip.split(':')[-1]) == 4:
				_port = int(ip.split(':')[-1])
				cherrypy.config.update({'server.socket_port': _port})
			else :
				_port = int(cherrypy.server.socket_port)
				print "Server: No valid port specified for service. Using default: %i" %_port
			cherrypy.config.update({'server.socket_host': _ip})
		else :
			_ip = str(cherrypy.server.socket_host)
			_port = int(cherrypy.server.socket_port)
		
		# pass in the global config object to the session:
		cherrypy.config.update({'tools.sessions.GLOBAL_CONFIG': self.config})
		
		#enable safemime tool to avoid upload timeouts
		try :
			safemime.init()
			cherrypy.config.update({'tools.safe_multipart.on': True})
		except:
			pass
		
		# set hostname in config
		if not self.config.hostname :
			self.config.hostname = str(cherrypy.server.socket_host).strip()+":"+str(cherrypy.server.socket_port).strip()			
		print 'Server: hostname is set to: ',self.config.hostname
		
		# report process pid and niceness
		print "Server: pid: ", os.getpid()
		# self.setProcessPriority(-3,'passwd')
		self.config.nice_value = self.getProcessPriority()
		print "Server: nice value: ", self.config.nice_value
		
		# report subversion rev. number
		self.config.svn_revision = self.getRevisionNumber()
		print "Server: SVN revision: ", self.config.svn_revision

		try :

			print 'Server: quickstarting apps at >> %s << (bounded ip/port: %s:%i)\n' \
			%(__localdir__,_ip,_port)
			# this will block
			if self.conf.root :
				# cherrypy.quickstart(self.conf.root,script_name='',config=self.conf.server_config_file)
				cherrypy.quickstart(self.conf.root, config=self.conf.server_config_file)
			else :
				# cherrypy.quickstart(script_name='',config=self.conf.server_config_file)
				cherrypy.quickstart(config=self.conf.server_config_file)
				

		except IOError, e:
			print "\n\n>>>Server::quickstart: IOError: Port not free: ",_ip,":",_port,"\nException: ",str(e)
			if self.getProcessPids(_port,kill=True) :
				self.quickstart(ip,retries-1)

		except cherrypy.server.interrupt, e: 
			print "\n\n>>>Server::quickstart: Server interrupt: Port may be used: ",_ip,":",_port,"\nException: ",str(e)
			if self.getProcessPids(_port,kill=True) :
				self.quickstart(ip,retries-1)

		except BaseException, e:
			print "\n\n>>>Server::quickstart: Exception caught while starting the service."			
			if 'Address already in use' in e : 
				print "Port already in use: ",_ip,":",_port
				if self.getProcessPids(_port,kill=True) :
					self.quickstart(ip,retries-1)
			else:
				print "Port may be in use: ",_ip,":",_port,"\nException: ",str(e)
				raise

		except :
			print "\n\n>>>Server::quickstart: Unknown exception caught while starting the service."
			print "Port may be in use: ",_ip,":",_port
			raise
		# cherrypy.quickstart(script_name='', config='config/server.cfg')		
		
		
	def mount(self,app,path_name=''):
		# cherrypy.tree.mount(requesthandler,config=appconfig)
		cherrypy.tree.mount(app,script_name=path_name,config=self.config.server_config_file)

		
	def getProcessPids(self,port,kill=False):
		'''Get the pid of the offending Python process given a port after an unsuccessful restart.'''
		print "Running lsof -i :"+str(port)," ...\n\n"
		command = "lsof -i :"+str(port)
		w = spopen(command,stdout=sp.PIPE,stderr=sp.PIPE,shell=True)
		se = w.stderr.readlines()
		result = w.stdout.readlines()
		exitcode = w.wait()
		if not result : 
			print "getProcessPid:: Unable to obtain process pid. (lsof returned nothing. exitcode: %s)" %str(exitcode)
			return False
		import pprint
		pprint.pprint(result)
		
		# get heading:
		ix = None
		head = result[0].upper()
		if 'PID' in head:
			head = filter(lambda x: x != str(), head.split(' '))
			head = map(lambda x: x.strip().replace(' ',''), head)
			if 'PID' in head : ix = head.index('PID')
		# get process pid
		pids = []
		for line in result :
			if 'python' in line.lower() :
				line = filter(lambda x: x != str(), line.split(' '))
				line = map(lambda x: x.strip().replace(' ',''), line)
				try :
					if ix : 
						pids.append(int(line[ix]))
					else:
						numbers = filter(lambda x: x.isdigit(), line)
						pids.append(int(numbers[0]))
				except:
					print 'Error parsing lsof results.' 
					return False
		print 'Pids found: ',pids
		# kill if specified
		if kill :
			pids_killed = []
			import signal
			for pid in pids:
				print 'Killing process: ',pid
				try :
					os.kill(pid,signal.SIGKILL)
					pids_killed.append(pid)
				except :
					print 'Failed: ',pid
			if pids_killed :
				print 'Processes killed:',pids_killed,' Waiting 10 secods...'
				import time
				time.sleep(10)
				return True
		return False

		
	def getProcessPriority(self):
		'''Get the priority of this Python process.'''
		pid = os.getpid()
		# print "Running ps -p %s -o nice" %str(pid)," ...\n\n"
		command = "ps -p %s -o nice" %str(pid)
		w = spopen(command,stdout=sp.PIPE,stderr=sp.PIPE,shell=True)
		se = w.stderr.readlines()
		result = w.stdout.readlines()
		exitcode = w.wait()
		if not result : 
			print "getProcessPriority:: Unable to obtain process proirity. (ps returned nothing. exitcode: %s)" %str(exitcode)
			return None
		try :
			return int(result[1].strip())
		except:
			print "getProcessPriority:: Error parsing process proirity value. (ps returned: \n%s, \nexitcode: %s)" %(str(result),str(exitcode))
			return None
			
			
	def setProcessPriority(self,value,passwd):
		'''Set the priority of this Python process.'''
		pid = os.getpid()
		command = "sudo -S renice -n %s -p %s" %(str(value),str(pid))
		w = spopen(command,stdout=sp.PIPE,stdin=sp.PIPE,stderr=sp.PIPE,shell=True)
		result,se = w.communicate(passwd)

		
	def getRevisionNumber(self):
		'''Get the Version control revision number by running svn info'''
		w = spopen("svn info",stdout=sp.PIPE,stdin=None,stderr=sp.PIPE,shell=True)
		try :
			result,err = w.communicate(None)
			rev = 0
			for line in result.split('\n'):
				if line.startswith('Revision') :
					rev = int(line.split(':')[1].strip())
			return rev
		except:
			return None
		

		
	def getHitCount(self):
		if hasattr(cherrypy,'session') :
			count = cherrypy.session.get('count', 0) + 1
			cherrypy.session['count'] = count
			return count
		else:
			return 0
		
	def getSessionID(self):
		if hasattr(cherrypy,'session') :
			self.getHitCount()
			return str(cherrypy.session.id)
		else:
			raise RuntimeError('server::getSessionID: No session information available.') 

	def getRequestSessionID(self):
		cookie = cherrypy.request.cookie
		if cookie.has_key('session_id') : 
			cookie = cookie['session_id'].output(header='')
			rsid = cookie.split('session_id=')[-1]
			return str(rsid)
		else :
			return None
			
	def validateSession(self):		
		if hasattr(cherrypy,'session') \
		and self.getSessionID() == self.getRequestSessionID() :
			return True
		else:
			if self.getRequestSessionID() is None :
				cookie = cherrypy.request.cookie
				log_msg = "SessionID mismatch: " + str(self.getSessionID()) + \
				" Received: None, Coockie was: " + str(cookie)
				cherrypy.log(log_msg)
			return False
			
	def adminSession(self):
		if hasattr(cherrypy,'session') and cherrypy.session.has_key('auth') and cherrypy.session['auth'] :
			return True
		return False
	
	def getFullPath(self,filelist):
		if (type(filelist) == list) :
			return map(lambda x: self.getFullPath(x),filelist)
		else:
			if type(filelist) != str and type(filelist) != unicode : 
				e ='server::getFullPath: Wrong path name in file list: %s' %(str(type(filelist)))
				raise RuntimeError(e)
			return os.path.join(self.getSessionPath(),filelist) 
		
	def getSessionPath(self):
		if hasattr(cherrypy,'session') :
			return cherrypy.session.get_session_path()
		else:
			raise RuntimeError('server::getSessionPath: No session information available.')
			
	def getSessionCount(self) :
		if hasattr(cherrypy,'session') :
			return len(cherrypy.session)
		else:
			raise RuntimeError('server::getSessionPath: No session information available.')
			
	def getActiveSessions(self) :
		if hasattr(cherrypy,'session') :
			return cherrypy.session.cache.keys()
		else:
			raise RuntimeError('server::getSessionPath: No session information available.')