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 /bitbake/lib/prserv | |
| 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>
Diffstat (limited to 'bitbake/lib/prserv')
| -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 |
3 files changed, 83 insertions, 31 deletions
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 | ||
