summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLianhao Lu <lianhao.lu@intel.com>2011-05-27 14:31:45 +0800
committerRichard Purdie <richard.purdie@linuxfoundation.org>2011-05-27 17:55:49 +0100
commitecdbd6ab03749f603ffbde2983b3e8ea3bbd73cd (patch)
treeafa8913e66b049d40848020e3da257a2f52137e0
parent859e21aac1584fe07e5cc7030e7f8918170291f9 (diff)
downloadpoky-ecdbd6ab03749f603ffbde2983b3e8ea3bbd73cd.tar.gz
Add PR service deamon to bitbake
Added the initial implementation of the server side PR service. (Bitbake rev: 4d0e79e5591ff58ce35c7fb96f6e9217ddc27466) Signed-off-by: Lianhao Lu <lianhao.lu@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--bitbake/bin/bitbake-prserv53
-rw-r--r--bitbake/lib/prserv/__init__.py11
-rw-r--r--bitbake/lib/prserv/db.py100
-rw-r--r--bitbake/lib/prserv/serv.py198
4 files changed, 362 insertions, 0 deletions
diff --git a/bitbake/bin/bitbake-prserv b/bitbake/bin/bitbake-prserv
new file mode 100644
index 0000000000..14073caf38
--- /dev/null
+++ b/bitbake/bin/bitbake-prserv
@@ -0,0 +1,53 @@
1#!/usr/bin/env python
2import os
3import sys,logging
4import optparse
5
6sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)),'lib'))
7
8import prserv
9import prserv.serv
10
11__version__="1.0.0"
12
13PRHOST_DEFAULT=''
14PRPORT_DEFAULT=8585
15
16def main():
17 parser = optparse.OptionParser(
18 version="Bitbake PR Service Core version %s, %%prog version %s" % (prserv.__version__, __version__),
19 usage = "%prog [options]")
20
21 parser.add_option("-f", "--file", help="database filename(default prserv.db)", action="store",
22 dest="dbfile", type="string", default="prserv.db")
23 parser.add_option("-l", "--log", help="log filename(default prserv.log)", action="store",
24 dest="logfile", type="string", default="prserv.log")
25 parser.add_option("--loglevel", help="logging level, i.e. CRITICAL, ERROR, WARNING, INFO, DEBUG",
26 action = "store", type="string", dest="loglevel", default = "WARNING")
27 parser.add_option("--start", help="start daemon",
28 action="store_true", dest="start", default="True")
29 parser.add_option("--stop", help="stop daemon",
30 action="store_false", dest="start")
31 parser.add_option("--host", help="ip address to bind", action="store",
32 dest="host", type="string", default=PRHOST_DEFAULT)
33 parser.add_option("--port", help="port number(default 8585)", action="store",
34 dest="port", type="int", default=PRPORT_DEFAULT)
35
36 options, args = parser.parse_args(sys.argv)
37
38 prserv.init_logger(os.path.abspath(options.logfile),options.loglevel)
39
40 if options.start:
41 prserv.serv.start_daemon(options)
42 else:
43 prserv.serv.stop_daemon()
44
45if __name__ == "__main__":
46 try:
47 ret = main()
48 except Exception:
49 ret = 1
50 import traceback
51 traceback.print_exc(5)
52 sys.exit(ret)
53
diff --git a/bitbake/lib/prserv/__init__.py b/bitbake/lib/prserv/__init__.py
new file mode 100644
index 0000000000..2837e135d7
--- /dev/null
+++ b/bitbake/lib/prserv/__init__.py
@@ -0,0 +1,11 @@
1__version__ = "1.0.0"
2
3import os, time
4import sys,logging
5
6def init_logger(logfile, loglevel):
7 numeric_level = getattr(logging, loglevel.upper(), None)
8 if not isinstance(numeric_level, int):
9 raise ValueError('Invalid log level: %s' % loglevel)
10 logging.basicConfig(level=numeric_level, filename=logfile)
11
diff --git a/bitbake/lib/prserv/db.py b/bitbake/lib/prserv/db.py
new file mode 100644
index 0000000000..bbee9316b2
--- /dev/null
+++ b/bitbake/lib/prserv/db.py
@@ -0,0 +1,100 @@
1import logging
2import os.path
3import errno
4import sys
5import warnings
6import sqlite3
7
8try:
9 import sqlite3
10except ImportError:
11 from pysqlite2 import dbapi2 as sqlite3
12
13sqlversion = sqlite3.sqlite_version_info
14if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
15 raise Exception("sqlite3 version 3.3.0 or later is required.")
16
17class NotFoundError(StandardError):
18 pass
19
20class PRTable():
21 def __init__(self,cursor,table):
22 self.cursor = cursor
23 self.table = table
24
25 #create the table
26 self._execute("CREATE TABLE IF NOT EXISTS %s \
27 (version TEXT NOT NULL, \
28 checksum TEXT NOT NULL, \
29 value INTEGER, \
30 PRIMARY KEY (version,checksum));"
31 % table)
32
33 def _execute(self, *query):
34 """Execute a query, waiting to acquire a lock if necessary"""
35 count = 0
36 while True:
37 try:
38 return self.cursor.execute(*query)
39 except sqlite3.OperationalError as exc:
40 if 'database is locked' in str(exc) and count < 500:
41 count = count + 1
42 continue
43 raise
44 except sqlite3.IntegrityError as exc:
45 print "Integrity error %s" % str(exc)
46 break
47
48 def getValue(self, version, checksum):
49 data=self._execute("SELECT value FROM %s WHERE version=? AND checksum=?;" % self.table,
50 (version,checksum))
51 row=data.fetchone()
52 if row != None:
53 return row[0]
54 else:
55 #no value found, try to insert
56 self._execute("INSERT INTO %s VALUES (?, ?, (select ifnull(max(value)+1,0) from %s where version=?));"
57 % (self.table,self.table),
58 (version,checksum,version))
59 data=self._execute("SELECT value FROM %s WHERE version=? AND checksum=?;" % self.table,
60 (version,checksum))
61 row=data.fetchone()
62 if row != None:
63 return row[0]
64 else:
65 raise NotFoundError
66
67class PRData(object):
68 """Object representing the PR database"""
69 def __init__(self, filename):
70 self.filename=os.path.abspath(filename)
71 #build directory hierarchy
72 try:
73 os.makedirs(os.path.dirname(self.filename))
74 except OSError as e:
75 if e.errno != errno.EEXIST:
76 raise e
77 self.connection=sqlite3.connect(self.filename, timeout=5,
78 isolation_level=None)
79 self.cursor=self.connection.cursor()
80 self._tables={}
81
82 def __del__(self):
83 print "PRData: closing DB %s" % self.filename
84 self.connection.close()
85
86 def __getitem__(self,tblname):
87 if not isinstance(tblname, basestring):
88 raise TypeError("tblname argument must be a string, not '%s'" %
89 type(tblname))
90 if tblname in self._tables:
91 return self._tables[tblname]
92 else:
93 tableobj = self._tables[tblname] = PRTable(self.cursor, tblname)
94 return tableobj
95
96 def __delitem__(self, tblname):
97 if tblname in self._tables:
98 del self._tables[tblname]
99 logging.info("drop table %s" % (tblname))
100 self.cursor.execute("DROP TABLE IF EXISTS %s;" % tblname)
diff --git a/bitbake/lib/prserv/serv.py b/bitbake/lib/prserv/serv.py
new file mode 100644
index 0000000000..ecafe4f94d
--- /dev/null
+++ b/bitbake/lib/prserv/serv.py
@@ -0,0 +1,198 @@
1import os,sys,logging
2import signal,time, atexit
3from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
4import xmlrpclib,sqlite3
5
6import bb.server.xmlrpc
7import prserv
8import prserv.db
9
10if sys.hexversion < 0x020600F0:
11 print("Sorry, python 2.6 or later is required.")
12 sys.exit(1)
13
14class Handler(SimpleXMLRPCRequestHandler):
15 def _dispatch(self,method,params):
16 try:
17 value=self.server.funcs[method](*params)
18 except:
19 import traceback
20 traceback.print_exc()
21 raise
22 return value
23
24class PRServer(SimpleXMLRPCServer):
25 pidfile="/tmp/PRServer.pid"
26 def __init__(self, dbfile, logfile, interface, daemon=True):
27 ''' constructor '''
28 SimpleXMLRPCServer.__init__(self, interface,
29 requestHandler=SimpleXMLRPCRequestHandler,
30 logRequests=False, allow_none=True)
31 self.dbfile=dbfile
32 self.daemon=daemon
33 self.logfile=logfile
34 self.host, self.port = self.socket.getsockname()
35 self.db=prserv.db.PRData(dbfile)
36 self.table=self.db["PRMAIN"]
37
38 self.register_function(self.getPR, "getPR")
39 self.register_function(self.quit, "quit")
40 self.register_function(self.ping, "ping")
41 self.register_introspection_functions()
42
43 def ping(self):
44 return not self.quit
45
46 def getPR(self, version, checksum):
47 try:
48 return self.table.getValue(version,checksum)
49 except prserv.NotFoundError:
50 logging.error("can not find value for (%s, %s)",version,checksum)
51 return None
52 except sqlite3.Error as exc:
53 logging.error(str(exc))
54 return None
55
56 def quit(self):
57 self.quit=True
58 return
59
60 def _serve_forever(self):
61 self.quit = False
62 self.timeout = 0.5
63 while not self.quit:
64 self.handle_request()
65
66 logging.info("PRServer: stopping...")
67 self.server_close()
68 return
69
70 def start(self):
71 if self.daemon is True:
72 logging.info("PRServer: starting daemon...")
73 self.daemonize()
74 else:
75 logging.info("PRServer: starting...")
76 self._serve_forever()
77
78 def delpid(self):
79 os.remove(PRServer.pidfile)
80
81 def daemonize(self):
82 """
83 See Advanced Programming in the UNIX, Sec 13.3
84 """
85 os.umask(0)
86
87 try:
88 pid = os.fork()
89 if pid > 0:
90 sys.exit(0)
91 except OSError,e:
92 sys.stderr.write("1st fork failed: %d %s\n" % (e.errno, e.strerror))
93 sys.exit(1)
94
95 os.setsid()
96 """
97 fork again to make sure the daemon is not session leader,
98 which prevents it from acquiring controlling terminal
99 """
100 try:
101 pid = os.fork()
102 if pid > 0: #parent
103 sys.exit(0)
104 except OSError,e:
105 sys.stderr.write("2nd fork failed: %d %s\n" % (e.errno, e.strerror))
106 sys.exit(1)
107
108 os.chdir("/")
109
110 sys.stdout.flush()
111 sys.stderr.flush()
112 si = file('/dev/null', 'r')
113 so = file(self.logfile, 'a+')
114 se = so
115 os.dup2(si.fileno(),sys.stdin.fileno())
116 os.dup2(so.fileno(),sys.stdout.fileno())
117 os.dup2(se.fileno(),sys.stderr.fileno())
118
119 # write pidfile
120 atexit.register(self.delpid)
121 pid = str(os.getpid())
122 pf = file(PRServer.pidfile, 'w+')
123 pf.write("%s\n" % pid)
124 pf.write("%s\n" % self.host)
125 pf.write("%s\n" % self.port)
126 pf.close()
127
128 self._serve_forever()
129
130class PRServerConnection():
131 def __init__(self, host, port):
132 self.connection = bb.server.xmlrpc._create_server(host, port)
133 self.host = host
134 self.port = port
135
136 def terminate(self):
137 # Don't wait for server indefinitely
138 import socket
139 socket.setdefaulttimeout(2)
140 try:
141 self.connection.quit()
142 except:
143 pass
144
145 def getPR(self, version, checksum):
146 return self.connection.getPR(version, checksum)
147
148 def ping(self):
149 return self.connection.ping()
150
151def start_daemon(options):
152 try:
153 pf = file(PRServer.pidfile,'r')
154 pid = int(pf.readline().strip())
155 pf.close()
156 except IOError:
157 pid = None
158
159 if pid:
160 sys.stderr.write("pidfile %s already exist. Daemon already running?\n"
161 % PRServer.pidfile)
162 sys.exit(1)
163
164 server = PRServer(options.dbfile, interface=(options.host, options.port),
165 logfile=os.path.abspath(options.logfile))
166 server.start()
167
168def stop_daemon():
169 try:
170 pf = file(PRServer.pidfile,'r')
171 pid = int(pf.readline().strip())
172 host = pf.readline().strip()
173 port = int(pf.readline().strip())
174 pf.close()
175 except IOError:
176 pid = None
177
178 if not pid:
179 sys.stderr.write("pidfile %s does not exist. Daemon not running?\n"
180 % PRServer.pidfile)
181 sys.exit(1)
182
183 PRServerConnection(host,port).terminate()
184 time.sleep(0.5)
185
186 try:
187 while 1:
188 os.kill(pid,signal.SIGTERM)
189 time.sleep(0.1)
190 except OSError, err:
191 err = str(err)
192 if err.find("No such process") > 0:
193 if os.path.exists(PRServer.pidfile):
194 os.remove(PRServer.pidfile)
195 else:
196 print err
197 sys.exit(1)
198