summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/prserv/serv.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/prserv/serv.py')
-rw-r--r--bitbake/lib/prserv/serv.py370
1 files changed, 370 insertions, 0 deletions
diff --git a/bitbake/lib/prserv/serv.py b/bitbake/lib/prserv/serv.py
new file mode 100644
index 0000000..1e170ce
--- /dev/null
+++ b/bitbake/lib/prserv/serv.py
@@ -0,0 +1,370 @@
1import os,sys,logging
2import signal, time, atexit, threading
3from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
4import xmlrpclib
5import threading
6import Queue
7
8try:
9 import sqlite3
10except ImportError:
11 from pysqlite2 import dbapi2 as sqlite3
12
13import bb.server.xmlrpc
14import prserv
15import prserv.db
16import errno
17
18logger = logging.getLogger("BitBake.PRserv")
19
20if sys.hexversion < 0x020600F0:
21 print("Sorry, python 2.6 or later is required.")
22 sys.exit(1)
23
24class Handler(SimpleXMLRPCRequestHandler):
25 def _dispatch(self,method,params):
26 try:
27 value=self.server.funcs[method](*params)
28 except:
29 import traceback
30 traceback.print_exc()
31 raise
32 return value
33
34PIDPREFIX = "/tmp/PRServer_%s_%s.pid"
35singleton = None
36
37
38class PRServer(SimpleXMLRPCServer):
39 def __init__(self, dbfile, logfile, interface, daemon=True):
40 ''' constructor '''
41 SimpleXMLRPCServer.__init__(self, interface,
42 logRequests=False, allow_none=True)
43 self.dbfile=dbfile
44 self.daemon=daemon
45 self.logfile=logfile
46 self.working_thread=None
47 self.host, self.port = self.socket.getsockname()
48 self.pidfile=PIDPREFIX % (self.host, self.port)
49
50 self.register_function(self.getPR, "getPR")
51 self.register_function(self.quit, "quit")
52 self.register_function(self.ping, "ping")
53 self.register_function(self.export, "export")
54 self.register_function(self.importone, "importone")
55 self.register_introspection_functions()
56
57 self.db = prserv.db.PRData(self.dbfile)
58 self.table = self.db["PRMAIN"]
59
60 self.requestqueue = Queue.Queue()
61 self.handlerthread = threading.Thread(target = self.process_request_thread)
62 self.handlerthread.daemon = False
63
64 def process_request_thread(self):
65 """Same as in BaseServer but as a thread.
66
67 In addition, exception handling is done here.
68
69 """
70 while True:
71 (request, client_address) = self.requestqueue.get()
72 try:
73 self.finish_request(request, client_address)
74 self.shutdown_request(request)
75 except:
76 self.handle_error(request, client_address)
77 self.shutdown_request(request)
78 self.table.sync()
79
80 def process_request(self, request, client_address):
81 self.requestqueue.put((request, client_address))
82
83 def export(self, version=None, pkgarch=None, checksum=None, colinfo=True):
84 try:
85 return self.table.export(version, pkgarch, checksum, colinfo)
86 except sqlite3.Error as exc:
87 logger.error(str(exc))
88 return None
89
90 def importone(self, version, pkgarch, checksum, value):
91 return self.table.importone(version, pkgarch, checksum, value)
92
93 def ping(self):
94 return not self.quit
95
96 def getinfo(self):
97 return (self.host, self.port)
98
99 def getPR(self, version, pkgarch, checksum):
100 try:
101 return self.table.getValue(version, pkgarch, checksum)
102 except prserv.NotFoundError:
103 logger.error("can not find value for (%s, %s)",version, checksum)
104 return None
105 except sqlite3.Error as exc:
106 logger.error(str(exc))
107 return None
108
109 def quit(self):
110 self.quit=True
111 return
112
113 def work_forever(self,):
114 self.quit = False
115 self.timeout = 0.5
116
117 logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" %
118 (self.dbfile, self.host, self.port, str(os.getpid())))
119
120 self.handlerthread.start()
121 while not self.quit:
122 self.handle_request()
123
124 self.table.sync()
125 logger.info("PRServer: stopping...")
126 self.server_close()
127 return
128
129 def start(self):
130 pid = self.daemonize()
131 # Ensure both the parent sees this and the child from the work_forever log entry above
132 logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" %
133 (self.dbfile, self.host, self.port, str(pid)))
134
135 def delpid(self):
136 os.remove(self.pidfile)
137
138 def daemonize(self):
139 """
140 See Advanced Programming in the UNIX, Sec 13.3
141 """
142 try:
143 pid = os.fork()
144 if pid > 0:
145 os.waitpid(pid, 0)
146 #parent return instead of exit to give control
147 return pid
148 except OSError as e:
149 raise Exception("%s [%d]" % (e.strerror, e.errno))
150
151 os.setsid()
152 """
153 fork again to make sure the daemon is not session leader,
154 which prevents it from acquiring controlling terminal
155 """
156 try:
157 pid = os.fork()
158 if pid > 0: #parent
159 os._exit(0)
160 except OSError as e:
161 raise Exception("%s [%d]" % (e.strerror, e.errno))
162
163 os.umask(0)
164 os.chdir("/")
165
166 sys.stdout.flush()
167 sys.stderr.flush()
168 si = file('/dev/null', 'r')
169 so = file(self.logfile, 'a+')
170 se = so
171 os.dup2(si.fileno(),sys.stdin.fileno())
172 os.dup2(so.fileno(),sys.stdout.fileno())
173 os.dup2(se.fileno(),sys.stderr.fileno())
174
175 # Clear out all log handlers prior to the fork() to avoid calling
176 # event handlers not part of the PRserver
177 for logger_iter in logging.Logger.manager.loggerDict.keys():
178 logging.getLogger(logger_iter).handlers = []
179
180 # Ensure logging makes it to the logfile
181 streamhandler = logging.StreamHandler()
182 streamhandler.setLevel(logging.DEBUG)
183 formatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
184 streamhandler.setFormatter(formatter)
185 logger.addHandler(streamhandler)
186
187 # write pidfile
188 pid = str(os.getpid())
189 pf = file(self.pidfile, 'w')
190 pf.write("%s\n" % pid)
191 pf.close()
192
193 self.work_forever()
194 self.delpid()
195 os._exit(0)
196
197class PRServSingleton(object):
198 def __init__(self, dbfile, logfile, interface):
199 self.dbfile = dbfile
200 self.logfile = logfile
201 self.interface = interface
202 self.host = None
203 self.port = None
204
205 def start(self):
206 self.prserv = PRServer(self.dbfile, self.logfile, self.interface)
207 self.prserv.start()
208 self.host, self.port = self.prserv.getinfo()
209
210 def getinfo(self):
211 return (self.host, self.port)
212
213class PRServerConnection(object):
214 def __init__(self, host, port):
215 if is_local_special(host, port):
216 host, port = singleton.getinfo()
217 self.host = host
218 self.port = port
219 self.connection, self.transport = bb.server.xmlrpc._create_server(self.host, self.port)
220
221 def terminate(self):
222 try:
223 logger.info("Terminating PRServer...")
224 self.connection.quit()
225 except Exception as exc:
226 sys.stderr.write("%s\n" % str(exc))
227
228 def getPR(self, version, pkgarch, checksum):
229 return self.connection.getPR(version, pkgarch, checksum)
230
231 def ping(self):
232 return self.connection.ping()
233
234 def export(self,version=None, pkgarch=None, checksum=None, colinfo=True):
235 return self.connection.export(version, pkgarch, checksum, colinfo)
236
237 def importone(self, version, pkgarch, checksum, value):
238 return self.connection.importone(version, pkgarch, checksum, value)
239
240 def getinfo(self):
241 return self.host, self.port
242
243def start_daemon(dbfile, host, port, logfile):
244 pidfile = PIDPREFIX % (host, port)
245 try:
246 pf = file(pidfile,'r')
247 pid = int(pf.readline().strip())
248 pf.close()
249 except IOError:
250 pid = None
251
252 if pid:
253 sys.stderr.write("pidfile %s already exist. Daemon already running?\n"
254 % pidfile)
255 return 1
256
257 server = PRServer(os.path.abspath(dbfile), os.path.abspath(logfile), (host,port))
258 server.start()
259 return 0
260
261def stop_daemon(host, port):
262 pidfile = PIDPREFIX % (host, port)
263 try:
264 pf = file(pidfile,'r')
265 pid = int(pf.readline().strip())
266 pf.close()
267 except IOError:
268 pid = None
269
270 if not pid:
271 sys.stderr.write("pidfile %s does not exist. Daemon not running?\n"
272 % pidfile)
273
274 try:
275 PRServerConnection(host, port).terminate()
276 except:
277 logger.critical("Stop PRService %s:%d failed" % (host,port))
278
279 try:
280 if pid:
281 wait_timeout = 0
282 print("Waiting for pr-server to exit.")
283 while is_running(pid) and wait_timeout < 50:
284 time.sleep(0.1)
285 wait_timeout += 1
286
287 if is_running(pid):
288 print("Sending SIGTERM to pr-server.")
289 os.kill(pid,signal.SIGTERM)
290 time.sleep(0.1)
291
292 if os.path.exists(pidfile):
293 os.remove(pidfile)
294
295 except OSError as e:
296 err = str(e)
297 if err.find("No such process") <= 0:
298 raise e
299
300 return 0
301
302def is_running(pid):
303 try:
304 os.kill(pid, 0)
305 except OSError as err:
306 if err.errno == errno.ESRCH:
307 return False
308 return True
309
310def is_local_special(host, port):
311 if host.strip().upper() == 'localhost'.upper() and (not port):
312 return True
313 else:
314 return False
315
316class PRServiceConfigError(Exception):
317 pass
318
319def auto_start(d):
320 global singleton
321
322 host_params = filter(None, (d.getVar('PRSERV_HOST', True) or '').split(':'))
323 if not host_params:
324 return None
325
326 if len(host_params) != 2:
327 logger.critical('\n'.join(['PRSERV_HOST: incorrect format',
328 'Usage: PRSERV_HOST = "<hostname>:<port>"']))
329 raise PRServiceConfigError
330
331 if is_local_special(host_params[0], int(host_params[1])) and not singleton:
332 import bb.utils
333 cachedir = (d.getVar("PERSISTENT_DIR", True) or d.getVar("CACHE", True))
334 if not cachedir:
335 logger.critical("Please set the 'PERSISTENT_DIR' or 'CACHE' variable")
336 raise PRServiceConfigError
337 bb.utils.mkdirhier(cachedir)
338 dbfile = os.path.join(cachedir, "prserv.sqlite3")
339 logfile = os.path.join(cachedir, "prserv.log")
340 singleton = PRServSingleton(os.path.abspath(dbfile), os.path.abspath(logfile), ("localhost",0))
341 singleton.start()
342 if singleton:
343 host, port = singleton.getinfo()
344 else:
345 host = host_params[0]
346 port = int(host_params[1])
347
348 try:
349 connection = PRServerConnection(host,port)
350 connection.ping()
351 realhost, realport = connection.getinfo()
352 return str(realhost) + ":" + str(realport)
353
354 except Exception:
355 logger.critical("PRservice %s:%d not available" % (host, port))
356 raise PRServiceConfigError
357
358def auto_shutdown(d=None):
359 global singleton
360 if singleton:
361 host, port = singleton.getinfo()
362 try:
363 PRServerConnection(host, port).terminate()
364 except:
365 logger.critical("Stop PRService %s:%d failed" % (host,port))
366 singleton = None
367
368def ping(host, port):
369 conn=PRServerConnection(host, port)
370 return conn.ping()