gyorgy@0
|
1 """
|
gyorgy@0
|
2 ServerWrapper.py
|
gyorgy@0
|
3
|
gyorgy@0
|
4 Created by George Fazekas, QMUL on 2009-05-09.
|
gyorgy@0
|
5 Copyright (c) 2009 QMUL. All rights reserved.
|
gyorgy@0
|
6 """
|
gyorgy@0
|
7 from __future__ import with_statement
|
gyorgy@0
|
8 import os,sys
|
gyorgy@0
|
9 import cherrypy
|
gyorgy@0
|
10 from cherrypy.lib import static
|
gyorgy@0
|
11 import subprocess as sp
|
gyorgy@0
|
12 from subprocess import Popen as spopen
|
gyorgy@0
|
13
|
gyorgy@0
|
14 try :
|
gyorgy@0
|
15 from cherrypy.lib import safemime
|
gyorgy@0
|
16 except:
|
gyorgy@0
|
17 print "Warning: import safemime failed. (Probably newer cherrypy version.)"
|
gyorgy@0
|
18 #safemime.init()
|
gyorgy@0
|
19 #root._cp_config = {'tools.safe_multipart.on': True}
|
gyorgy@0
|
20
|
gyorgy@0
|
21 __localdir__ = os.path.realpath(os.path.dirname(os.path.abspath(__file__)))
|
gyorgy@0
|
22
|
gyorgy@0
|
23 # ============================================
|
gyorgy@0
|
24 # Shared Configuration data container
|
gyorgy@0
|
25 # ============================================
|
gyorgy@0
|
26
|
gyorgy@0
|
27 class Config(object):
|
gyorgy@0
|
28 '''A shared config object'''
|
gyorgy@0
|
29
|
gyorgy@0
|
30 def __init__(self,root=None,local=False,conf='not set'):
|
gyorgy@0
|
31 self.cherrypyVersion = 0
|
gyorgy@0
|
32 self.nice_value = 0
|
gyorgy@0
|
33 self.svn_revision = 0
|
gyorgy@0
|
34 self.hostname = str()
|
gyorgy@0
|
35 self.localdir = __localdir__
|
gyorgy@0
|
36 self.server_config_file = str()
|
gyorgy@0
|
37 self.local = local
|
gyorgy@0
|
38 self.server_config_file = conf
|
gyorgy@0
|
39 self.root = root
|
gyorgy@0
|
40
|
gyorgy@0
|
41
|
gyorgy@0
|
42 # ============================================
|
gyorgy@0
|
43 # Cherrypy ServerWrapper
|
gyorgy@0
|
44 # ============================================
|
gyorgy@0
|
45
|
gyorgy@0
|
46 # This is a thin wrapper on cherrypy
|
gyorgy@0
|
47 class ServerWrapper(object):
|
gyorgy@0
|
48
|
gyorgy@0
|
49 def __init__(self,config):
|
gyorgy@0
|
50 self.conf = config
|
gyorgy@0
|
51 self.config = self.conf
|
gyorgy@0
|
52
|
gyorgy@0
|
53
|
gyorgy@0
|
54 def quickstart(self,ip=None,retries=5):
|
gyorgy@0
|
55 '''Start fx-test'''
|
gyorgy@0
|
56 if not retries :
|
gyorgy@0
|
57 print 'Server: Error: Retry limit reached while trying to start service.'
|
gyorgy@0
|
58 # TDOD: maybe we want to shut the daemon here as well... to avoid external loop
|
gyorgy@0
|
59 return False
|
gyorgy@0
|
60 self.config.cherrypyVersion = cherrypy.__version__
|
gyorgy@0
|
61 print 'Server: Cherrypy version: %s' %str(cherrypy.__version__)
|
gyorgy@0
|
62
|
gyorgy@0
|
63 cherrypy.server.socket_timeout = 60
|
gyorgy@0
|
64 cherrypy.config.update({'tools.staticdir.root': __localdir__})
|
gyorgy@0
|
65 # cherrypy.config.update('./config/server.cfg')
|
gyorgy@0
|
66 cherrypy.config.update(self.config.server_config_file)
|
gyorgy@0
|
67 if type(ip) == str and ip.count('.') == 3:
|
gyorgy@0
|
68 _ip = ip.split(':')[0]
|
gyorgy@0
|
69 if ip.count(':') and len(ip.split(':')[-1]) == 4:
|
gyorgy@0
|
70 _port = int(ip.split(':')[-1])
|
gyorgy@0
|
71 cherrypy.config.update({'server.socket_port': _port})
|
gyorgy@0
|
72 else :
|
gyorgy@0
|
73 _port = int(cherrypy.server.socket_port)
|
gyorgy@0
|
74 print "Server: No valid port specified for service. Using default: %i" %_port
|
gyorgy@0
|
75 cherrypy.config.update({'server.socket_host': _ip})
|
gyorgy@0
|
76 else :
|
gyorgy@0
|
77 _ip = str(cherrypy.server.socket_host)
|
gyorgy@0
|
78 _port = int(cherrypy.server.socket_port)
|
gyorgy@0
|
79
|
gyorgy@0
|
80 # pass in the global config object to the session:
|
gyorgy@0
|
81 cherrypy.config.update({'tools.sessions.GLOBAL_CONFIG': self.config})
|
gyorgy@0
|
82
|
gyorgy@0
|
83 #enable safemime tool to avoid upload timeouts
|
gyorgy@0
|
84 try :
|
gyorgy@0
|
85 safemime.init()
|
gyorgy@0
|
86 cherrypy.config.update({'tools.safe_multipart.on': True})
|
gyorgy@0
|
87 except:
|
gyorgy@0
|
88 pass
|
gyorgy@0
|
89
|
gyorgy@0
|
90 # set hostname in config
|
gyorgy@0
|
91 if not self.config.hostname :
|
gyorgy@0
|
92 self.config.hostname = str(cherrypy.server.socket_host).strip()+":"+str(cherrypy.server.socket_port).strip()
|
gyorgy@0
|
93 print 'Server: hostname is set to: ',self.config.hostname
|
gyorgy@0
|
94
|
gyorgy@0
|
95 # report process pid and niceness
|
gyorgy@0
|
96 print "Server: pid: ", os.getpid()
|
gyorgy@0
|
97 # self.setProcessPriority(-3,'passwd')
|
gyorgy@0
|
98 self.config.nice_value = self.getProcessPriority()
|
gyorgy@0
|
99 print "Server: nice value: ", self.config.nice_value
|
gyorgy@0
|
100
|
gyorgy@0
|
101 # report subversion rev. number
|
gyorgy@0
|
102 self.config.svn_revision = self.getRevisionNumber()
|
gyorgy@0
|
103 print "Server: SVN revision: ", self.config.svn_revision
|
gyorgy@0
|
104
|
gyorgy@0
|
105 try :
|
gyorgy@0
|
106
|
gyorgy@0
|
107 print 'Server: quickstarting apps at >> %s << (bounded ip/port: %s:%i)\n' \
|
gyorgy@0
|
108 %(__localdir__,_ip,_port)
|
gyorgy@0
|
109 # this will block
|
gyorgy@0
|
110 if self.conf.root :
|
gyorgy@0
|
111 # cherrypy.quickstart(self.conf.root,script_name='',config=self.conf.server_config_file)
|
gyorgy@0
|
112 cherrypy.quickstart(self.conf.root, config=self.conf.server_config_file)
|
gyorgy@0
|
113 else :
|
gyorgy@0
|
114 # cherrypy.quickstart(script_name='',config=self.conf.server_config_file)
|
gyorgy@0
|
115 cherrypy.quickstart(config=self.conf.server_config_file)
|
gyorgy@0
|
116
|
gyorgy@0
|
117
|
gyorgy@0
|
118 except IOError, e:
|
gyorgy@0
|
119 print "\n\n>>>Server::quickstart: IOError: Port not free: ",_ip,":",_port,"\nException: ",str(e)
|
gyorgy@0
|
120 if self.getProcessPids(_port,kill=True) :
|
gyorgy@0
|
121 self.quickstart(ip,retries-1)
|
gyorgy@0
|
122
|
gyorgy@0
|
123 except cherrypy.server.interrupt, e:
|
gyorgy@0
|
124 print "\n\n>>>Server::quickstart: Server interrupt: Port may be used: ",_ip,":",_port,"\nException: ",str(e)
|
gyorgy@0
|
125 if self.getProcessPids(_port,kill=True) :
|
gyorgy@0
|
126 self.quickstart(ip,retries-1)
|
gyorgy@0
|
127
|
gyorgy@0
|
128 except BaseException, e:
|
gyorgy@0
|
129 print "\n\n>>>Server::quickstart: Exception caught while starting the service."
|
gyorgy@0
|
130 if 'Address already in use' in e :
|
gyorgy@0
|
131 print "Port already in use: ",_ip,":",_port
|
gyorgy@0
|
132 if self.getProcessPids(_port,kill=True) :
|
gyorgy@0
|
133 self.quickstart(ip,retries-1)
|
gyorgy@0
|
134 else:
|
gyorgy@0
|
135 print "Port may be in use: ",_ip,":",_port,"\nException: ",str(e)
|
gyorgy@0
|
136 raise
|
gyorgy@0
|
137
|
gyorgy@0
|
138 except :
|
gyorgy@0
|
139 print "\n\n>>>Server::quickstart: Unknown exception caught while starting the service."
|
gyorgy@0
|
140 print "Port may be in use: ",_ip,":",_port
|
gyorgy@0
|
141 raise
|
gyorgy@0
|
142 # cherrypy.quickstart(script_name='', config='config/server.cfg')
|
gyorgy@0
|
143
|
gyorgy@0
|
144
|
gyorgy@0
|
145 def mount(self,app,path_name=''):
|
gyorgy@0
|
146 # cherrypy.tree.mount(requesthandler,config=appconfig)
|
gyorgy@0
|
147 cherrypy.tree.mount(app,script_name=path_name,config=self.config.server_config_file)
|
gyorgy@0
|
148
|
gyorgy@0
|
149
|
gyorgy@0
|
150 def getProcessPids(self,port,kill=False):
|
gyorgy@0
|
151 '''Get the pid of the offending Python process given a port after an unsuccessful restart.'''
|
gyorgy@0
|
152 print "Running lsof -i :"+str(port)," ...\n\n"
|
gyorgy@0
|
153 command = "lsof -i :"+str(port)
|
gyorgy@0
|
154 w = spopen(command,stdout=sp.PIPE,stderr=sp.PIPE,shell=True)
|
gyorgy@0
|
155 se = w.stderr.readlines()
|
gyorgy@0
|
156 result = w.stdout.readlines()
|
gyorgy@0
|
157 exitcode = w.wait()
|
gyorgy@0
|
158 if not result :
|
gyorgy@0
|
159 print "getProcessPid:: Unable to obtain process pid. (lsof returned nothing. exitcode: %s)" %str(exitcode)
|
gyorgy@0
|
160 return False
|
gyorgy@0
|
161 import pprint
|
gyorgy@0
|
162 pprint.pprint(result)
|
gyorgy@0
|
163
|
gyorgy@0
|
164 # get heading:
|
gyorgy@0
|
165 ix = None
|
gyorgy@0
|
166 head = result[0].upper()
|
gyorgy@0
|
167 if 'PID' in head:
|
gyorgy@0
|
168 head = filter(lambda x: x != str(), head.split(' '))
|
gyorgy@0
|
169 head = map(lambda x: x.strip().replace(' ',''), head)
|
gyorgy@0
|
170 if 'PID' in head : ix = head.index('PID')
|
gyorgy@0
|
171 # get process pid
|
gyorgy@0
|
172 pids = []
|
gyorgy@0
|
173 for line in result :
|
gyorgy@0
|
174 if 'python' in line.lower() :
|
gyorgy@0
|
175 line = filter(lambda x: x != str(), line.split(' '))
|
gyorgy@0
|
176 line = map(lambda x: x.strip().replace(' ',''), line)
|
gyorgy@0
|
177 try :
|
gyorgy@0
|
178 if ix :
|
gyorgy@0
|
179 pids.append(int(line[ix]))
|
gyorgy@0
|
180 else:
|
gyorgy@0
|
181 numbers = filter(lambda x: x.isdigit(), line)
|
gyorgy@0
|
182 pids.append(int(numbers[0]))
|
gyorgy@0
|
183 except:
|
gyorgy@0
|
184 print 'Error parsing lsof results.'
|
gyorgy@0
|
185 return False
|
gyorgy@0
|
186 print 'Pids found: ',pids
|
gyorgy@0
|
187 # kill if specified
|
gyorgy@0
|
188 if kill :
|
gyorgy@0
|
189 pids_killed = []
|
gyorgy@0
|
190 import signal
|
gyorgy@0
|
191 for pid in pids:
|
gyorgy@0
|
192 print 'Killing process: ',pid
|
gyorgy@0
|
193 try :
|
gyorgy@0
|
194 os.kill(pid,signal.SIGKILL)
|
gyorgy@0
|
195 pids_killed.append(pid)
|
gyorgy@0
|
196 except :
|
gyorgy@0
|
197 print 'Failed: ',pid
|
gyorgy@0
|
198 if pids_killed :
|
gyorgy@0
|
199 print 'Processes killed:',pids_killed,' Waiting 10 secods...'
|
gyorgy@0
|
200 import time
|
gyorgy@0
|
201 time.sleep(10)
|
gyorgy@0
|
202 return True
|
gyorgy@0
|
203 return False
|
gyorgy@0
|
204
|
gyorgy@0
|
205
|
gyorgy@0
|
206 def getProcessPriority(self):
|
gyorgy@0
|
207 '''Get the priority of this Python process.'''
|
gyorgy@0
|
208 pid = os.getpid()
|
gyorgy@0
|
209 # print "Running ps -p %s -o nice" %str(pid)," ...\n\n"
|
gyorgy@0
|
210 command = "ps -p %s -o nice" %str(pid)
|
gyorgy@0
|
211 w = spopen(command,stdout=sp.PIPE,stderr=sp.PIPE,shell=True)
|
gyorgy@0
|
212 se = w.stderr.readlines()
|
gyorgy@0
|
213 result = w.stdout.readlines()
|
gyorgy@0
|
214 exitcode = w.wait()
|
gyorgy@0
|
215 if not result :
|
gyorgy@0
|
216 print "getProcessPriority:: Unable to obtain process proirity. (ps returned nothing. exitcode: %s)" %str(exitcode)
|
gyorgy@0
|
217 return None
|
gyorgy@0
|
218 try :
|
gyorgy@0
|
219 return int(result[1].strip())
|
gyorgy@0
|
220 except:
|
gyorgy@0
|
221 print "getProcessPriority:: Error parsing process proirity value. (ps returned: \n%s, \nexitcode: %s)" %(str(result),str(exitcode))
|
gyorgy@0
|
222 return None
|
gyorgy@0
|
223
|
gyorgy@0
|
224
|
gyorgy@0
|
225 def setProcessPriority(self,value,passwd):
|
gyorgy@0
|
226 '''Set the priority of this Python process.'''
|
gyorgy@0
|
227 pid = os.getpid()
|
gyorgy@0
|
228 command = "sudo -S renice -n %s -p %s" %(str(value),str(pid))
|
gyorgy@0
|
229 w = spopen(command,stdout=sp.PIPE,stdin=sp.PIPE,stderr=sp.PIPE,shell=True)
|
gyorgy@0
|
230 result,se = w.communicate(passwd)
|
gyorgy@0
|
231
|
gyorgy@0
|
232
|
gyorgy@0
|
233 def getRevisionNumber(self):
|
gyorgy@0
|
234 '''Get the Version control revision number by running svn info'''
|
gyorgy@0
|
235 w = spopen("svn info",stdout=sp.PIPE,stdin=None,stderr=sp.PIPE,shell=True)
|
gyorgy@0
|
236 try :
|
gyorgy@0
|
237 result,err = w.communicate(None)
|
gyorgy@0
|
238 rev = 0
|
gyorgy@0
|
239 for line in result.split('\n'):
|
gyorgy@0
|
240 if line.startswith('Revision') :
|
gyorgy@0
|
241 rev = int(line.split(':')[1].strip())
|
gyorgy@0
|
242 return rev
|
gyorgy@0
|
243 except:
|
gyorgy@0
|
244 return None
|
gyorgy@0
|
245
|
gyorgy@0
|
246
|
gyorgy@0
|
247
|
gyorgy@0
|
248 def getHitCount(self):
|
gyorgy@0
|
249 if hasattr(cherrypy,'session') :
|
gyorgy@0
|
250 count = cherrypy.session.get('count', 0) + 1
|
gyorgy@0
|
251 cherrypy.session['count'] = count
|
gyorgy@0
|
252 return count
|
gyorgy@0
|
253 else:
|
gyorgy@0
|
254 return 0
|
gyorgy@0
|
255
|
gyorgy@0
|
256 def getSessionID(self):
|
gyorgy@0
|
257 if hasattr(cherrypy,'session') :
|
gyorgy@0
|
258 self.getHitCount()
|
gyorgy@0
|
259 return str(cherrypy.session.id)
|
gyorgy@0
|
260 else:
|
gyorgy@0
|
261 raise RuntimeError('server::getSessionID: No session information available.')
|
gyorgy@0
|
262
|
gyorgy@0
|
263 def getRequestSessionID(self):
|
gyorgy@0
|
264 cookie = cherrypy.request.cookie
|
gyorgy@0
|
265 if cookie.has_key('session_id') :
|
gyorgy@0
|
266 cookie = cookie['session_id'].output(header='')
|
gyorgy@0
|
267 rsid = cookie.split('session_id=')[-1]
|
gyorgy@0
|
268 return str(rsid)
|
gyorgy@0
|
269 else :
|
gyorgy@0
|
270 return None
|
gyorgy@0
|
271
|
gyorgy@0
|
272 def validateSession(self):
|
gyorgy@0
|
273 if hasattr(cherrypy,'session') \
|
gyorgy@0
|
274 and self.getSessionID() == self.getRequestSessionID() :
|
gyorgy@0
|
275 return True
|
gyorgy@0
|
276 else:
|
gyorgy@0
|
277 if self.getRequestSessionID() is None :
|
gyorgy@0
|
278 cookie = cherrypy.request.cookie
|
gyorgy@0
|
279 log_msg = "SessionID mismatch: " + str(self.getSessionID()) + \
|
gyorgy@0
|
280 " Received: None, Coockie was: " + str(cookie)
|
gyorgy@0
|
281 cherrypy.log(log_msg)
|
gyorgy@0
|
282 return False
|
gyorgy@0
|
283
|
gyorgy@0
|
284 def adminSession(self):
|
gyorgy@0
|
285 if hasattr(cherrypy,'session') and cherrypy.session.has_key('auth') and cherrypy.session['auth'] :
|
gyorgy@0
|
286 return True
|
gyorgy@0
|
287 return False
|
gyorgy@0
|
288
|
gyorgy@0
|
289 def getFullPath(self,filelist):
|
gyorgy@0
|
290 if (type(filelist) == list) :
|
gyorgy@0
|
291 return map(lambda x: self.getFullPath(x),filelist)
|
gyorgy@0
|
292 else:
|
gyorgy@0
|
293 if type(filelist) != str and type(filelist) != unicode :
|
gyorgy@0
|
294 e ='server::getFullPath: Wrong path name in file list: %s' %(str(type(filelist)))
|
gyorgy@0
|
295 raise RuntimeError(e)
|
gyorgy@0
|
296 return os.path.join(self.getSessionPath(),filelist)
|
gyorgy@0
|
297
|
gyorgy@0
|
298 def getSessionPath(self):
|
gyorgy@0
|
299 if hasattr(cherrypy,'session') :
|
gyorgy@0
|
300 return cherrypy.session.get_session_path()
|
gyorgy@0
|
301 else:
|
gyorgy@0
|
302 raise RuntimeError('server::getSessionPath: No session information available.')
|
gyorgy@0
|
303
|
gyorgy@0
|
304 def getSessionCount(self) :
|
gyorgy@0
|
305 if hasattr(cherrypy,'session') :
|
gyorgy@0
|
306 return len(cherrypy.session)
|
gyorgy@0
|
307 else:
|
gyorgy@0
|
308 raise RuntimeError('server::getSessionPath: No session information available.')
|
gyorgy@0
|
309
|
gyorgy@0
|
310 def getActiveSessions(self) :
|
gyorgy@0
|
311 if hasattr(cherrypy,'session') :
|
gyorgy@0
|
312 return cherrypy.session.cache.keys()
|
gyorgy@0
|
313 else:
|
gyorgy@0
|
314 raise RuntimeError('server::getSessionPath: No session information available.')
|
gyorgy@0
|
315
|
gyorgy@0
|
316 |