summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Barker <pbarker@konsulko.com>2021-08-19 12:46:44 -0400
committerRichard Purdie <richard.purdie@linuxfoundation.org>2021-08-23 08:30:55 +0100
commit295b75cf1cc17f9ab3990d9640a72af83a593a77 (patch)
tree326994f9e533bea5c008174aee38d6ed9fb2a5e9
parentfb3b05fe8da817967c9f90d4c4c0c1fee87c9f01 (diff)
downloadpoky-295b75cf1cc17f9ab3990d9640a72af83a593a77.tar.gz
bitbake: prserv: Add read-only mode
[YOCTO #13659] (Bitbake rev: 44287430b9804fcbf2440f85a2424792140e4dc9) Signed-off-by: Paul Barker <pbarker@konsulko.com> [updated for asyncrpc changes] Signed-off-by: Scott Murray <scott.murray@konsulko.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rwxr-xr-xbitbake/bin/bitbake-prserv4
-rw-r--r--bitbake/lib/prserv/client.py9
-rw-r--r--bitbake/lib/prserv/db.py65
-rw-r--r--bitbake/lib/prserv/serv.py40
4 files changed, 86 insertions, 32 deletions
diff --git a/bitbake/bin/bitbake-prserv b/bitbake/bin/bitbake-prserv
index 1e9b6cbc1b..bef5ef6897 100755
--- a/bitbake/bin/bitbake-prserv
+++ b/bitbake/bin/bitbake-prserv
@@ -36,12 +36,14 @@ def main():
36 dest="host", type="string", default=PRHOST_DEFAULT) 36 dest="host", type="string", default=PRHOST_DEFAULT)
37 parser.add_option("--port", help="port number(default: 8585)", action="store", 37 parser.add_option("--port", help="port number(default: 8585)", action="store",
38 dest="port", type="int", default=PRPORT_DEFAULT) 38 dest="port", type="int", default=PRPORT_DEFAULT)
39 parser.add_option("-r", "--read-only", help="open database in read-only mode",
40 action="store_true")
39 41
40 options, args = parser.parse_args(sys.argv) 42 options, args = parser.parse_args(sys.argv)
41 prserv.init_logger(os.path.abspath(options.logfile),options.loglevel) 43 prserv.init_logger(os.path.abspath(options.logfile),options.loglevel)
42 44
43 if options.start: 45 if options.start:
44 ret=prserv.serv.start_daemon(options.dbfile, options.host, options.port,os.path.abspath(options.logfile)) 46 ret=prserv.serv.start_daemon(options.dbfile, options.host, options.port,os.path.abspath(options.logfile), options.read_only)
45 elif options.stop: 47 elif options.stop:
46 ret=prserv.serv.stop_daemon(options.host, options.port) 48 ret=prserv.serv.stop_daemon(options.host, options.port)
47 else: 49 else:
diff --git a/bitbake/lib/prserv/client.py b/bitbake/lib/prserv/client.py
index 285dce72f6..a3f19ddafc 100644
--- a/bitbake/lib/prserv/client.py
+++ b/bitbake/lib/prserv/client.py
@@ -32,10 +32,17 @@ class PRAsyncClient(bb.asyncrpc.AsyncClient):
32 if response: 32 if response:
33 return (response['metainfo'], response['datainfo']) 33 return (response['metainfo'], response['datainfo'])
34 34
35 async def is_readonly(self):
36 response = await self.send_message(
37 {'is-readonly': {}}
38 )
39 if response:
40 return response['readonly']
41
35class PRClient(bb.asyncrpc.Client): 42class PRClient(bb.asyncrpc.Client):
36 def __init__(self): 43 def __init__(self):
37 super().__init__() 44 super().__init__()
38 self._add_methods('getPR', 'importone', 'export') 45 self._add_methods('getPR', 'importone', 'export', 'is_readonly')
39 46
40 def _get_async_client(self): 47 def _get_async_client(self):
41 return PRAsyncClient() 48 return PRAsyncClient()
diff --git a/bitbake/lib/prserv/db.py b/bitbake/lib/prserv/db.py
index cb2a2461e0..2710d4a225 100644
--- a/bitbake/lib/prserv/db.py
+++ b/bitbake/lib/prserv/db.py
@@ -30,21 +30,29 @@ if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
30# 30#
31 31
32class PRTable(object): 32class PRTable(object):
33 def __init__(self, conn, table, nohist): 33 def __init__(self, conn, table, nohist, read_only):
34 self.conn = conn 34 self.conn = conn
35 self.nohist = nohist 35 self.nohist = nohist
36 self.read_only = read_only
36 self.dirty = False 37 self.dirty = False
37 if nohist: 38 if nohist:
38 self.table = "%s_nohist" % table 39 self.table = "%s_nohist" % table
39 else: 40 else:
40 self.table = "%s_hist" % table 41 self.table = "%s_hist" % table
41 42
42 self._execute("CREATE TABLE IF NOT EXISTS %s \ 43 if self.read_only:
43 (version TEXT NOT NULL, \ 44 table_exists = self._execute(
44 pkgarch TEXT NOT NULL, \ 45 "SELECT count(*) FROM sqlite_master \
45 checksum TEXT NOT NULL, \ 46 WHERE type='table' AND name='%s'" % (self.table))
46 value INTEGER, \ 47 if not table_exists:
47 PRIMARY KEY (version, pkgarch, checksum));" % self.table) 48 raise prserv.NotFoundError
49 else:
50 self._execute("CREATE TABLE IF NOT EXISTS %s \
51 (version TEXT NOT NULL, \
52 pkgarch TEXT NOT NULL, \
53 checksum TEXT NOT NULL, \
54 value INTEGER, \
55 PRIMARY KEY (version, pkgarch, checksum));" % self.table)
48 56
49 def _execute(self, *query): 57 def _execute(self, *query):
50 """Execute a query, waiting to acquire a lock if necessary""" 58 """Execute a query, waiting to acquire a lock if necessary"""
@@ -59,8 +67,9 @@ class PRTable(object):
59 raise exc 67 raise exc
60 68
61 def sync(self): 69 def sync(self):
62 self.conn.commit() 70 if not self.read_only:
63 self._execute("BEGIN EXCLUSIVE TRANSACTION") 71 self.conn.commit()
72 self._execute("BEGIN EXCLUSIVE TRANSACTION")
64 73
65 def sync_if_dirty(self): 74 def sync_if_dirty(self):
66 if self.dirty: 75 if self.dirty:
@@ -75,6 +84,15 @@ class PRTable(object):
75 return row[0] 84 return row[0]
76 else: 85 else:
77 #no value found, try to insert 86 #no value found, try to insert
87 if self.read_only:
88 data = self._execute("SELECT ifnull(max(value)+1,0) FROM %s where version=? AND pkgarch=?;" % (self.table),
89 (version, pkgarch))
90 row = data.fetchone()
91 if row is not None:
92 return row[0]
93 else:
94 return 0
95
78 try: 96 try:
79 self._execute("INSERT INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1,0) from %s where version=? AND pkgarch=?));" 97 self._execute("INSERT INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1,0) from %s where version=? AND pkgarch=?));"
80 % (self.table,self.table), 98 % (self.table,self.table),
@@ -103,6 +121,15 @@ class PRTable(object):
103 return row[0] 121 return row[0]
104 else: 122 else:
105 #no value found, try to insert 123 #no value found, try to insert
124 if self.read_only:
125 data = self._execute("SELECT ifnull(max(value)+1,0) FROM %s where version=? AND pkgarch=?;" % (self.table),
126 (version, pkgarch))
127 row = data.fetchone()
128 if row is not None:
129 return row[0]
130 else:
131 return 0
132
106 try: 133 try:
107 self._execute("INSERT OR REPLACE INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1,0) from %s where version=? AND pkgarch=?));" 134 self._execute("INSERT OR REPLACE INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1,0) from %s where version=? AND pkgarch=?));"
108 % (self.table,self.table), 135 % (self.table,self.table),
@@ -128,6 +155,9 @@ class PRTable(object):
128 return self._getValueHist(version, pkgarch, checksum) 155 return self._getValueHist(version, pkgarch, checksum)
129 156
130 def _importHist(self, version, pkgarch, checksum, value): 157 def _importHist(self, version, pkgarch, checksum, value):
158 if self.read_only:
159 return None
160
131 val = None 161 val = None
132 data = self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table, 162 data = self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table,
133 (version, pkgarch, checksum)) 163 (version, pkgarch, checksum))
@@ -152,6 +182,9 @@ class PRTable(object):
152 return val 182 return val
153 183
154 def _importNohist(self, version, pkgarch, checksum, value): 184 def _importNohist(self, version, pkgarch, checksum, value):
185 if self.read_only:
186 return None
187
155 try: 188 try:
156 #try to insert 189 #try to insert
157 self._execute("INSERT INTO %s VALUES (?, ?, ?, ?);" % (self.table), 190 self._execute("INSERT INTO %s VALUES (?, ?, ?, ?);" % (self.table),
@@ -245,19 +278,23 @@ class PRTable(object):
245 278
246class PRData(object): 279class PRData(object):
247 """Object representing the PR database""" 280 """Object representing the PR database"""
248 def __init__(self, filename, nohist=True): 281 def __init__(self, filename, nohist=True, read_only=False):
249 self.filename=os.path.abspath(filename) 282 self.filename=os.path.abspath(filename)
250 self.nohist=nohist 283 self.nohist=nohist
284 self.read_only = read_only
251 #build directory hierarchy 285 #build directory hierarchy
252 try: 286 try:
253 os.makedirs(os.path.dirname(self.filename)) 287 os.makedirs(os.path.dirname(self.filename))
254 except OSError as e: 288 except OSError as e:
255 if e.errno != errno.EEXIST: 289 if e.errno != errno.EEXIST:
256 raise e 290 raise e
257 self.connection=sqlite3.connect(self.filename, isolation_level="EXCLUSIVE", check_same_thread = False) 291 uri = "file:%s%s" % (self.filename, "?mode=ro" if self.read_only else "")
292 logger.debug("Opening PRServ database '%s'" % (uri))
293 self.connection=sqlite3.connect(uri, uri=True, isolation_level="EXCLUSIVE", check_same_thread = False)
258 self.connection.row_factory=sqlite3.Row 294 self.connection.row_factory=sqlite3.Row
259 self.connection.execute("pragma synchronous = off;") 295 if not self.read_only:
260 self.connection.execute("PRAGMA journal_mode = MEMORY;") 296 self.connection.execute("pragma synchronous = off;")
297 self.connection.execute("PRAGMA journal_mode = MEMORY;")
261 self._tables={} 298 self._tables={}
262 299
263 def disconnect(self): 300 def disconnect(self):
@@ -270,7 +307,7 @@ class PRData(object):
270 if tblname in self._tables: 307 if tblname in self._tables:
271 return self._tables[tblname] 308 return self._tables[tblname]
272 else: 309 else:
273 tableobj = self._tables[tblname] = PRTable(self.connection, tblname, self.nohist) 310 tableobj = self._tables[tblname] = PRTable(self.connection, tblname, self.nohist, self.read_only)
274 return tableobj 311 return tableobj
275 312
276 def __delitem__(self, tblname): 313 def __delitem__(self, tblname):
diff --git a/bitbake/lib/prserv/serv.py b/bitbake/lib/prserv/serv.py
index 1fa4e1766c..17ae40967c 100644
--- a/bitbake/lib/prserv/serv.py
+++ b/bitbake/lib/prserv/serv.py
@@ -18,14 +18,16 @@ PIDPREFIX = "/tmp/PRServer_%s_%s.pid"
18singleton = None 18singleton = None
19 19
20class PRServerClient(bb.asyncrpc.AsyncServerConnection): 20class PRServerClient(bb.asyncrpc.AsyncServerConnection):
21 def __init__(self, reader, writer, table): 21 def __init__(self, reader, writer, table, read_only):
22 super().__init__(reader, writer, 'PRSERVICE', logger) 22 super().__init__(reader, writer, 'PRSERVICE', logger)
23 self.handlers.update({ 23 self.handlers.update({
24 'get-pr': self.handle_get_pr, 24 'get-pr': self.handle_get_pr,
25 'import-one': self.handle_import_one, 25 'import-one': self.handle_import_one,
26 'export': self.handle_export, 26 'export': self.handle_export,
27 'is-readonly': self.handle_is_readonly,
27 }) 28 })
28 self.table = table 29 self.table = table
30 self.read_only = read_only
29 31
30 def validate_proto_version(self): 32 def validate_proto_version(self):
31 return (self.proto_version == (1, 0)) 33 return (self.proto_version == (1, 0))
@@ -56,16 +58,17 @@ class PRServerClient(bb.asyncrpc.AsyncServerConnection):
56 self.write_message(response) 58 self.write_message(response)
57 59
58 async def handle_import_one(self, request): 60 async def handle_import_one(self, request):
59 version = request['version'] 61 response = None
60 pkgarch = request['pkgarch'] 62 if not self.read_only:
61 checksum = request['checksum'] 63 version = request['version']
62 value = request['value'] 64 pkgarch = request['pkgarch']
65 checksum = request['checksum']
66 value = request['value']
67
68 value = self.table.importone(version, pkgarch, checksum, value)
69 if value is not None:
70 response = {'value': value}
63 71
64 value = self.table.importone(version, pkgarch, checksum, value)
65 if value is not None:
66 response = {'value': value}
67 else:
68 response = None
69 self.write_message(response) 72 self.write_message(response)
70 73
71 async def handle_export(self, request): 74 async def handle_export(self, request):
@@ -83,20 +86,25 @@ class PRServerClient(bb.asyncrpc.AsyncServerConnection):
83 response = {'metainfo': metainfo, 'datainfo': datainfo} 86 response = {'metainfo': metainfo, 'datainfo': datainfo}
84 self.write_message(response) 87 self.write_message(response)
85 88
89 async def handle_is_readonly(self, request):
90 response = {'readonly': self.read_only}
91 self.write_message(response)
92
86class PRServer(bb.asyncrpc.AsyncServer): 93class PRServer(bb.asyncrpc.AsyncServer):
87 def __init__(self, dbfile): 94 def __init__(self, dbfile, read_only=False):
88 super().__init__(logger) 95 super().__init__(logger)
89 self.dbfile = dbfile 96 self.dbfile = dbfile
90 self.table = None 97 self.table = None
98 self.read_only = read_only
91 99
92 def accept_client(self, reader, writer): 100 def accept_client(self, reader, writer):
93 return PRServerClient(reader, writer, self.table) 101 return PRServerClient(reader, writer, self.table, self.read_only)
94 102
95 def _serve_forever(self): 103 def _serve_forever(self):
96 self.db = prserv.db.PRData(self.dbfile) 104 self.db = prserv.db.PRData(self.dbfile, read_only=self.read_only)
97 self.table = self.db["PRMAIN"] 105 self.table = self.db["PRMAIN"]
98 106
99 logger.debug("Started PRServer with DBfile: %s, Address: %s, PID: %s" % 107 logger.info("Started PRServer with DBfile: %s, Address: %s, PID: %s" %
100 (self.dbfile, self.address, str(os.getpid()))) 108 (self.dbfile, self.address, str(os.getpid())))
101 109
102 super()._serve_forever() 110 super()._serve_forever()
@@ -194,7 +202,7 @@ def run_as_daemon(func, pidfile, logfile):
194 os.remove(pidfile) 202 os.remove(pidfile)
195 os._exit(0) 203 os._exit(0)
196 204
197def start_daemon(dbfile, host, port, logfile): 205def start_daemon(dbfile, host, port, logfile, read_only=False):
198 ip = socket.gethostbyname(host) 206 ip = socket.gethostbyname(host)
199 pidfile = PIDPREFIX % (ip, port) 207 pidfile = PIDPREFIX % (ip, port)
200 try: 208 try:
@@ -210,7 +218,7 @@ def start_daemon(dbfile, host, port, logfile):
210 218
211 dbfile = os.path.abspath(dbfile) 219 dbfile = os.path.abspath(dbfile)
212 def daemon_main(): 220 def daemon_main():
213 server = PRServer(dbfile) 221 server = PRServer(dbfile, read_only=read_only)
214 server.start_tcp_server(host, port) 222 server.start_tcp_server(host, port)
215 server.serve_forever() 223 server.serve_forever()
216 224