diff options
author | Joshua Watt <JPEWhacker@gmail.com> | 2023-11-03 08:26:33 -0600 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-11-09 17:33:03 +0000 |
commit | 3a2c5a6fa2e0081c28d5f2f43e1d9a79d093ea37 (patch) | |
tree | f596b84a7295cd8421fcee447a10e2c082b94c72 | |
parent | 8cfb94c06cdfe3e6f0ec1ce0154951108bc3df94 (diff) | |
download | poky-3a2c5a6fa2e0081c28d5f2f43e1d9a79d093ea37.tar.gz |
bitbake: hashserv: Add db-usage API
Adds an API to query the server for the usage of the database (e.g. how
many rows are present in each table)
(Bitbake rev: c9c1224447e147e0de92953bc85cea75670b898c)
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rwxr-xr-x | bitbake/bin/bitbake-hashclient | 16 | ||||
-rw-r--r-- | bitbake/lib/hashserv/client.py | 5 | ||||
-rw-r--r-- | bitbake/lib/hashserv/server.py | 5 | ||||
-rw-r--r-- | bitbake/lib/hashserv/sqlalchemy.py | 14 | ||||
-rw-r--r-- | bitbake/lib/hashserv/sqlite.py | 37 | ||||
-rw-r--r-- | bitbake/lib/hashserv/tests.py | 9 |
6 files changed, 86 insertions, 0 deletions
diff --git a/bitbake/bin/bitbake-hashclient b/bitbake/bin/bitbake-hashclient index cfbc197e52..5d65c7bc56 100755 --- a/bitbake/bin/bitbake-hashclient +++ b/bitbake/bin/bitbake-hashclient | |||
@@ -161,6 +161,19 @@ def main(): | |||
161 | r = client.delete_user(args.username) | 161 | r = client.delete_user(args.username) |
162 | print_user(r) | 162 | print_user(r) |
163 | 163 | ||
164 | def handle_get_db_usage(args, client): | ||
165 | usage = client.get_db_usage() | ||
166 | print(usage) | ||
167 | tables = sorted(usage.keys()) | ||
168 | print("{name:20}| {rows:20}".format(name="Table name", rows="Rows")) | ||
169 | print(("-" * 20) + "+" + ("-" * 20)) | ||
170 | for t in tables: | ||
171 | print("{name:20}| {rows:<20}".format(name=t, rows=usage[t]["rows"])) | ||
172 | print() | ||
173 | |||
174 | total_rows = sum(t["rows"] for t in usage.values()) | ||
175 | print(f"Total rows: {total_rows}") | ||
176 | |||
164 | parser = argparse.ArgumentParser(description='Hash Equivalence Client') | 177 | parser = argparse.ArgumentParser(description='Hash Equivalence Client') |
165 | parser.add_argument('--address', default=DEFAULT_ADDRESS, help='Server address (default "%(default)s")') | 178 | parser.add_argument('--address', default=DEFAULT_ADDRESS, help='Server address (default "%(default)s")') |
166 | parser.add_argument('--log', default='WARNING', help='Set logging level') | 179 | parser.add_argument('--log', default='WARNING', help='Set logging level') |
@@ -223,6 +236,9 @@ def main(): | |||
223 | delete_user_parser.add_argument("--username", "-u", help="Username", required=True) | 236 | delete_user_parser.add_argument("--username", "-u", help="Username", required=True) |
224 | delete_user_parser.set_defaults(func=handle_delete_user) | 237 | delete_user_parser.set_defaults(func=handle_delete_user) |
225 | 238 | ||
239 | db_usage_parser = subparsers.add_parser('get-db-usage', help="Database Usage") | ||
240 | db_usage_parser.set_defaults(func=handle_get_db_usage) | ||
241 | |||
226 | args = parser.parse_args() | 242 | args = parser.parse_args() |
227 | 243 | ||
228 | logger = logging.getLogger('hashserv') | 244 | logger = logging.getLogger('hashserv') |
diff --git a/bitbake/lib/hashserv/client.py b/bitbake/lib/hashserv/client.py index 4457f8e50d..5e0a462b1c 100644 --- a/bitbake/lib/hashserv/client.py +++ b/bitbake/lib/hashserv/client.py | |||
@@ -186,6 +186,10 @@ class AsyncClient(bb.asyncrpc.AsyncClient): | |||
186 | self.saved_become_user = username | 186 | self.saved_become_user = username |
187 | return result | 187 | return result |
188 | 188 | ||
189 | async def get_db_usage(self): | ||
190 | await self._set_mode(self.MODE_NORMAL) | ||
191 | return (await self.invoke({"get-db-usage": {}}))["usage"] | ||
192 | |||
189 | 193 | ||
190 | class Client(bb.asyncrpc.Client): | 194 | class Client(bb.asyncrpc.Client): |
191 | def __init__(self, username=None, password=None): | 195 | def __init__(self, username=None, password=None): |
@@ -214,6 +218,7 @@ class Client(bb.asyncrpc.Client): | |||
214 | "new_user", | 218 | "new_user", |
215 | "delete_user", | 219 | "delete_user", |
216 | "become_user", | 220 | "become_user", |
221 | "get_db_usage", | ||
217 | ) | 222 | ) |
218 | 223 | ||
219 | def _get_async_client(self): | 224 | def _get_async_client(self): |
diff --git a/bitbake/lib/hashserv/server.py b/bitbake/lib/hashserv/server.py index ca419a1abf..c5b9797e4e 100644 --- a/bitbake/lib/hashserv/server.py +++ b/bitbake/lib/hashserv/server.py | |||
@@ -249,6 +249,7 @@ class ServerClient(bb.asyncrpc.AsyncServerConnection): | |||
249 | "get-outhash": self.handle_get_outhash, | 249 | "get-outhash": self.handle_get_outhash, |
250 | "get-stream": self.handle_get_stream, | 250 | "get-stream": self.handle_get_stream, |
251 | "get-stats": self.handle_get_stats, | 251 | "get-stats": self.handle_get_stats, |
252 | "get-db-usage": self.handle_get_db_usage, | ||
252 | # Not always read-only, but internally checks if the server is | 253 | # Not always read-only, but internally checks if the server is |
253 | # read-only | 254 | # read-only |
254 | "report": self.handle_report, | 255 | "report": self.handle_report, |
@@ -567,6 +568,10 @@ class ServerClient(bb.asyncrpc.AsyncServerConnection): | |||
567 | oldest = datetime.now() - timedelta(seconds=-max_age) | 568 | oldest = datetime.now() - timedelta(seconds=-max_age) |
568 | return {"count": await self.db.clean_unused(oldest)} | 569 | return {"count": await self.db.clean_unused(oldest)} |
569 | 570 | ||
571 | @permissions(DB_ADMIN_PERM) | ||
572 | async def handle_get_db_usage(self, request): | ||
573 | return {"usage": await self.db.get_usage()} | ||
574 | |||
570 | # The authentication API is always allowed | 575 | # The authentication API is always allowed |
571 | async def handle_auth(self, request): | 576 | async def handle_auth(self, request): |
572 | username = str(request["username"]) | 577 | username = str(request["username"]) |
diff --git a/bitbake/lib/hashserv/sqlalchemy.py b/bitbake/lib/hashserv/sqlalchemy.py index bfd8a8446e..818b51951b 100644 --- a/bitbake/lib/hashserv/sqlalchemy.py +++ b/bitbake/lib/hashserv/sqlalchemy.py | |||
@@ -27,6 +27,7 @@ from sqlalchemy import ( | |||
27 | and_, | 27 | and_, |
28 | delete, | 28 | delete, |
29 | update, | 29 | update, |
30 | func, | ||
30 | ) | 31 | ) |
31 | import sqlalchemy.engine | 32 | import sqlalchemy.engine |
32 | from sqlalchemy.orm import declarative_base | 33 | from sqlalchemy.orm import declarative_base |
@@ -401,3 +402,16 @@ class Database(object): | |||
401 | async with self.db.begin(): | 402 | async with self.db.begin(): |
402 | result = await self.db.execute(statement) | 403 | result = await self.db.execute(statement) |
403 | return result.rowcount != 0 | 404 | return result.rowcount != 0 |
405 | |||
406 | async def get_usage(self): | ||
407 | usage = {} | ||
408 | async with self.db.begin() as session: | ||
409 | for name, table in Base.metadata.tables.items(): | ||
410 | statement = select(func.count()).select_from(table) | ||
411 | self.logger.debug("%s", statement) | ||
412 | result = await self.db.execute(statement) | ||
413 | usage[name] = { | ||
414 | "rows": result.scalar(), | ||
415 | } | ||
416 | |||
417 | return usage | ||
diff --git a/bitbake/lib/hashserv/sqlite.py b/bitbake/lib/hashserv/sqlite.py index 414ee8ffb8..dfdccbbaa0 100644 --- a/bitbake/lib/hashserv/sqlite.py +++ b/bitbake/lib/hashserv/sqlite.py | |||
@@ -120,6 +120,18 @@ class Database(object): | |||
120 | self.db = sqlite3.connect(self.dbname) | 120 | self.db = sqlite3.connect(self.dbname) |
121 | self.db.row_factory = sqlite3.Row | 121 | self.db.row_factory = sqlite3.Row |
122 | 122 | ||
123 | with closing(self.db.cursor()) as cursor: | ||
124 | cursor.execute("SELECT sqlite_version()") | ||
125 | |||
126 | version = [] | ||
127 | for v in cursor.fetchone()[0].split("."): | ||
128 | try: | ||
129 | version.append(int(v)) | ||
130 | except ValueError: | ||
131 | version.append(v) | ||
132 | |||
133 | self.sqlite_version = tuple(version) | ||
134 | |||
123 | async def __aenter__(self): | 135 | async def __aenter__(self): |
124 | return self | 136 | return self |
125 | 137 | ||
@@ -362,3 +374,28 @@ class Database(object): | |||
362 | ) | 374 | ) |
363 | self.db.commit() | 375 | self.db.commit() |
364 | return cursor.rowcount != 0 | 376 | return cursor.rowcount != 0 |
377 | |||
378 | async def get_usage(self): | ||
379 | usage = {} | ||
380 | with closing(self.db.cursor()) as cursor: | ||
381 | if self.sqlite_version >= (3, 33): | ||
382 | table_name = "sqlite_schema" | ||
383 | else: | ||
384 | table_name = "sqlite_master" | ||
385 | |||
386 | cursor.execute( | ||
387 | f""" | ||
388 | SELECT name FROM {table_name} WHERE type = 'table' AND name NOT LIKE 'sqlite_%' | ||
389 | """ | ||
390 | ) | ||
391 | for row in cursor.fetchall(): | ||
392 | cursor.execute( | ||
393 | """ | ||
394 | SELECT COUNT() FROM %s | ||
395 | """ | ||
396 | % row["name"], | ||
397 | ) | ||
398 | usage[row["name"]] = { | ||
399 | "rows": cursor.fetchone()[0], | ||
400 | } | ||
401 | return usage | ||
diff --git a/bitbake/lib/hashserv/tests.py b/bitbake/lib/hashserv/tests.py index 311b7b7772..9d5bec2454 100644 --- a/bitbake/lib/hashserv/tests.py +++ b/bitbake/lib/hashserv/tests.py | |||
@@ -767,6 +767,15 @@ class HashEquivalenceCommonTests(object): | |||
767 | with self.auth_perms("@user-admin") as client: | 767 | with self.auth_perms("@user-admin") as client: |
768 | become = client.become_user(client.username) | 768 | become = client.become_user(client.username) |
769 | 769 | ||
770 | def test_get_db_usage(self): | ||
771 | usage = self.client.get_db_usage() | ||
772 | |||
773 | self.assertTrue(isinstance(usage, dict)) | ||
774 | for name in usage.keys(): | ||
775 | self.assertTrue(isinstance(usage[name], dict)) | ||
776 | self.assertIn("rows", usage[name]) | ||
777 | self.assertTrue(isinstance(usage[name]["rows"], int)) | ||
778 | |||
770 | 779 | ||
771 | class TestHashEquivalenceUnixServer(HashEquivalenceTestSetup, HashEquivalenceCommonTests, unittest.TestCase): | 780 | class TestHashEquivalenceUnixServer(HashEquivalenceTestSetup, HashEquivalenceCommonTests, unittest.TestCase): |
772 | def get_server_addr(self, server_idx): | 781 | def get_server_addr(self, server_idx): |