From ecdbd6ab03749f603ffbde2983b3e8ea3bbd73cd Mon Sep 17 00:00:00 2001 From: Lianhao Lu Date: Fri, 27 May 2011 14:31:45 +0800 Subject: Add PR service deamon to bitbake Added the initial implementation of the server side PR service. (Bitbake rev: 4d0e79e5591ff58ce35c7fb96f6e9217ddc27466) Signed-off-by: Lianhao Lu Signed-off-by: Richard Purdie --- bitbake/bin/bitbake-prserv | 53 +++++++++++ bitbake/lib/prserv/__init__.py | 11 +++ bitbake/lib/prserv/db.py | 100 +++++++++++++++++++++ bitbake/lib/prserv/serv.py | 198 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 362 insertions(+) create mode 100644 bitbake/bin/bitbake-prserv create mode 100644 bitbake/lib/prserv/__init__.py create mode 100644 bitbake/lib/prserv/db.py create mode 100644 bitbake/lib/prserv/serv.py (limited to 'bitbake') 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 @@ +#!/usr/bin/env python +import os +import sys,logging +import optparse + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)),'lib')) + +import prserv +import prserv.serv + +__version__="1.0.0" + +PRHOST_DEFAULT='' +PRPORT_DEFAULT=8585 + +def main(): + parser = optparse.OptionParser( + version="Bitbake PR Service Core version %s, %%prog version %s" % (prserv.__version__, __version__), + usage = "%prog [options]") + + parser.add_option("-f", "--file", help="database filename(default prserv.db)", action="store", + dest="dbfile", type="string", default="prserv.db") + parser.add_option("-l", "--log", help="log filename(default prserv.log)", action="store", + dest="logfile", type="string", default="prserv.log") + parser.add_option("--loglevel", help="logging level, i.e. CRITICAL, ERROR, WARNING, INFO, DEBUG", + action = "store", type="string", dest="loglevel", default = "WARNING") + parser.add_option("--start", help="start daemon", + action="store_true", dest="start", default="True") + parser.add_option("--stop", help="stop daemon", + action="store_false", dest="start") + parser.add_option("--host", help="ip address to bind", action="store", + dest="host", type="string", default=PRHOST_DEFAULT) + parser.add_option("--port", help="port number(default 8585)", action="store", + dest="port", type="int", default=PRPORT_DEFAULT) + + options, args = parser.parse_args(sys.argv) + + prserv.init_logger(os.path.abspath(options.logfile),options.loglevel) + + if options.start: + prserv.serv.start_daemon(options) + else: + prserv.serv.stop_daemon() + +if __name__ == "__main__": + try: + ret = main() + except Exception: + ret = 1 + import traceback + traceback.print_exc(5) + sys.exit(ret) + 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 @@ +__version__ = "1.0.0" + +import os, time +import sys,logging + +def init_logger(logfile, loglevel): + numeric_level = getattr(logging, loglevel.upper(), None) + if not isinstance(numeric_level, int): + raise ValueError('Invalid log level: %s' % loglevel) + logging.basicConfig(level=numeric_level, filename=logfile) + 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 @@ +import logging +import os.path +import errno +import sys +import warnings +import sqlite3 + +try: + import sqlite3 +except ImportError: + from pysqlite2 import dbapi2 as sqlite3 + +sqlversion = sqlite3.sqlite_version_info +if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3): + raise Exception("sqlite3 version 3.3.0 or later is required.") + +class NotFoundError(StandardError): + pass + +class PRTable(): + def __init__(self,cursor,table): + self.cursor = cursor + self.table = table + + #create the table + self._execute("CREATE TABLE IF NOT EXISTS %s \ + (version TEXT NOT NULL, \ + checksum TEXT NOT NULL, \ + value INTEGER, \ + PRIMARY KEY (version,checksum));" + % table) + + def _execute(self, *query): + """Execute a query, waiting to acquire a lock if necessary""" + count = 0 + while True: + try: + return self.cursor.execute(*query) + except sqlite3.OperationalError as exc: + if 'database is locked' in str(exc) and count < 500: + count = count + 1 + continue + raise + except sqlite3.IntegrityError as exc: + print "Integrity error %s" % str(exc) + break + + def getValue(self, version, checksum): + data=self._execute("SELECT value FROM %s WHERE version=? AND checksum=?;" % self.table, + (version,checksum)) + row=data.fetchone() + if row != None: + return row[0] + else: + #no value found, try to insert + self._execute("INSERT INTO %s VALUES (?, ?, (select ifnull(max(value)+1,0) from %s where version=?));" + % (self.table,self.table), + (version,checksum,version)) + data=self._execute("SELECT value FROM %s WHERE version=? AND checksum=?;" % self.table, + (version,checksum)) + row=data.fetchone() + if row != None: + return row[0] + else: + raise NotFoundError + +class PRData(object): + """Object representing the PR database""" + def __init__(self, filename): + self.filename=os.path.abspath(filename) + #build directory hierarchy + try: + os.makedirs(os.path.dirname(self.filename)) + except OSError as e: + if e.errno != errno.EEXIST: + raise e + self.connection=sqlite3.connect(self.filename, timeout=5, + isolation_level=None) + self.cursor=self.connection.cursor() + self._tables={} + + def __del__(self): + print "PRData: closing DB %s" % self.filename + self.connection.close() + + def __getitem__(self,tblname): + if not isinstance(tblname, basestring): + raise TypeError("tblname argument must be a string, not '%s'" % + type(tblname)) + if tblname in self._tables: + return self._tables[tblname] + else: + tableobj = self._tables[tblname] = PRTable(self.cursor, tblname) + return tableobj + + def __delitem__(self, tblname): + if tblname in self._tables: + del self._tables[tblname] + logging.info("drop table %s" % (tblname)) + 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 @@ +import os,sys,logging +import signal,time, atexit +from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler +import xmlrpclib,sqlite3 + +import bb.server.xmlrpc +import prserv +import prserv.db + +if sys.hexversion < 0x020600F0: + print("Sorry, python 2.6 or later is required.") + sys.exit(1) + +class Handler(SimpleXMLRPCRequestHandler): + def _dispatch(self,method,params): + try: + value=self.server.funcs[method](*params) + except: + import traceback + traceback.print_exc() + raise + return value + +class PRServer(SimpleXMLRPCServer): + pidfile="/tmp/PRServer.pid" + def __init__(self, dbfile, logfile, interface, daemon=True): + ''' constructor ''' + SimpleXMLRPCServer.__init__(self, interface, + requestHandler=SimpleXMLRPCRequestHandler, + logRequests=False, allow_none=True) + self.dbfile=dbfile + self.daemon=daemon + self.logfile=logfile + self.host, self.port = self.socket.getsockname() + self.db=prserv.db.PRData(dbfile) + self.table=self.db["PRMAIN"] + + self.register_function(self.getPR, "getPR") + self.register_function(self.quit, "quit") + self.register_function(self.ping, "ping") + self.register_introspection_functions() + + def ping(self): + return not self.quit + + def getPR(self, version, checksum): + try: + return self.table.getValue(version,checksum) + except prserv.NotFoundError: + logging.error("can not find value for (%s, %s)",version,checksum) + return None + except sqlite3.Error as exc: + logging.error(str(exc)) + return None + + def quit(self): + self.quit=True + return + + def _serve_forever(self): + self.quit = False + self.timeout = 0.5 + while not self.quit: + self.handle_request() + + logging.info("PRServer: stopping...") + self.server_close() + return + + def start(self): + if self.daemon is True: + logging.info("PRServer: starting daemon...") + self.daemonize() + else: + logging.info("PRServer: starting...") + self._serve_forever() + + def delpid(self): + os.remove(PRServer.pidfile) + + def daemonize(self): + """ + See Advanced Programming in the UNIX, Sec 13.3 + """ + os.umask(0) + + try: + pid = os.fork() + if pid > 0: + sys.exit(0) + except OSError,e: + sys.stderr.write("1st fork failed: %d %s\n" % (e.errno, e.strerror)) + sys.exit(1) + + os.setsid() + """ + fork again to make sure the daemon is not session leader, + which prevents it from acquiring controlling terminal + """ + try: + pid = os.fork() + if pid > 0: #parent + sys.exit(0) + except OSError,e: + sys.stderr.write("2nd fork failed: %d %s\n" % (e.errno, e.strerror)) + sys.exit(1) + + os.chdir("/") + + sys.stdout.flush() + sys.stderr.flush() + si = file('/dev/null', 'r') + so = file(self.logfile, 'a+') + se = so + os.dup2(si.fileno(),sys.stdin.fileno()) + os.dup2(so.fileno(),sys.stdout.fileno()) + os.dup2(se.fileno(),sys.stderr.fileno()) + + # write pidfile + atexit.register(self.delpid) + pid = str(os.getpid()) + pf = file(PRServer.pidfile, 'w+') + pf.write("%s\n" % pid) + pf.write("%s\n" % self.host) + pf.write("%s\n" % self.port) + pf.close() + + self._serve_forever() + +class PRServerConnection(): + def __init__(self, host, port): + self.connection = bb.server.xmlrpc._create_server(host, port) + self.host = host + self.port = port + + def terminate(self): + # Don't wait for server indefinitely + import socket + socket.setdefaulttimeout(2) + try: + self.connection.quit() + except: + pass + + def getPR(self, version, checksum): + return self.connection.getPR(version, checksum) + + def ping(self): + return self.connection.ping() + +def start_daemon(options): + try: + pf = file(PRServer.pidfile,'r') + pid = int(pf.readline().strip()) + pf.close() + except IOError: + pid = None + + if pid: + sys.stderr.write("pidfile %s already exist. Daemon already running?\n" + % PRServer.pidfile) + sys.exit(1) + + server = PRServer(options.dbfile, interface=(options.host, options.port), + logfile=os.path.abspath(options.logfile)) + server.start() + +def stop_daemon(): + try: + pf = file(PRServer.pidfile,'r') + pid = int(pf.readline().strip()) + host = pf.readline().strip() + port = int(pf.readline().strip()) + pf.close() + except IOError: + pid = None + + if not pid: + sys.stderr.write("pidfile %s does not exist. Daemon not running?\n" + % PRServer.pidfile) + sys.exit(1) + + PRServerConnection(host,port).terminate() + time.sleep(0.5) + + try: + while 1: + os.kill(pid,signal.SIGTERM) + time.sleep(0.1) + except OSError, err: + err = str(err) + if err.find("No such process") > 0: + if os.path.exists(PRServer.pidfile): + os.remove(PRServer.pidfile) + else: + print err + sys.exit(1) + -- cgit v1.2.3-54-g00ecf