diff options
author | Paul Barker <pbarker@konsulko.com> | 2021-08-19 12:46:44 -0400 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-08-23 08:30:55 +0100 |
commit | 295b75cf1cc17f9ab3990d9640a72af83a593a77 (patch) | |
tree | 326994f9e533bea5c008174aee38d6ed9fb2a5e9 | |
parent | fb3b05fe8da817967c9f90d4c4c0c1fee87c9f01 (diff) | |
download | poky-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-x | bitbake/bin/bitbake-prserv | 4 | ||||
-rw-r--r-- | bitbake/lib/prserv/client.py | 9 | ||||
-rw-r--r-- | bitbake/lib/prserv/db.py | 65 | ||||
-rw-r--r-- | bitbake/lib/prserv/serv.py | 40 |
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 | |||
35 | class PRClient(bb.asyncrpc.Client): | 42 | class 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 | ||
32 | class PRTable(object): | 32 | class 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 | ||
246 | class PRData(object): | 279 | class 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" | |||
18 | singleton = None | 18 | singleton = None |
19 | 19 | ||
20 | class PRServerClient(bb.asyncrpc.AsyncServerConnection): | 20 | class 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 | |||
86 | class PRServer(bb.asyncrpc.AsyncServer): | 93 | class 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 | ||
197 | def start_daemon(dbfile, host, port, logfile): | 205 | def 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 | ||