summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/prserv/serv.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/prserv/serv.py')
-rw-r--r--bitbake/lib/prserv/serv.py134
1 files changed, 117 insertions, 17 deletions
diff --git a/bitbake/lib/prserv/serv.py b/bitbake/lib/prserv/serv.py
index dc4be5b620..05573d06cc 100644
--- a/bitbake/lib/prserv/serv.py
+++ b/bitbake/lib/prserv/serv.py
@@ -12,6 +12,7 @@ import sqlite3
12import prserv 12import prserv
13import prserv.db 13import prserv.db
14import errno 14import errno
15from . import create_async_client, revision_smaller, increase_revision
15import bb.asyncrpc 16import bb.asyncrpc
16 17
17logger = logging.getLogger("BitBake.PRserv") 18logger = logging.getLogger("BitBake.PRserv")
@@ -51,8 +52,9 @@ class PRServerClient(bb.asyncrpc.AsyncServerConnection):
51 version = request["version"] 52 version = request["version"]
52 pkgarch = request["pkgarch"] 53 pkgarch = request["pkgarch"]
53 checksum = request["checksum"] 54 checksum = request["checksum"]
55 history = request["history"]
54 56
55 value = self.server.table.find_value(version, pkgarch, checksum) 57 value = self.server.table.find_value(version, pkgarch, checksum, history)
56 return {"value": value} 58 return {"value": value}
57 59
58 async def handle_test_package(self, request): 60 async def handle_test_package(self, request):
@@ -68,22 +70,110 @@ class PRServerClient(bb.asyncrpc.AsyncServerConnection):
68 version = request["version"] 70 version = request["version"]
69 pkgarch = request["pkgarch"] 71 pkgarch = request["pkgarch"]
70 72
71 value = self.server.table.find_max_value(version, pkgarch) 73 value = self.server.table.find_package_max_value(version, pkgarch)
72 return {"value": value} 74 return {"value": value}
73 75
74 async def handle_get_pr(self, request): 76 async def handle_get_pr(self, request):
75 version = request["version"] 77 version = request["version"]
76 pkgarch = request["pkgarch"] 78 pkgarch = request["pkgarch"]
77 checksum = request["checksum"] 79 checksum = request["checksum"]
80 history = request["history"]
78 81
79 response = None 82 if self.upstream_client is None:
80 try: 83 value = self.server.table.get_value(version, pkgarch, checksum, history)
81 value = self.server.table.get_value(version, pkgarch, checksum) 84 return {"value": value}
82 response = {"value": value}
83 except prserv.NotFoundError:
84 self.logger.error("failure storing value in database for (%s, %s)",version, checksum)
85 85
86 return response 86 # We have an upstream server.
87 # Check whether the local server already knows the requested configuration.
88 # If the configuration is a new one, the generated value we will add will
89 # depend on what's on the upstream server. That's why we're calling find_value()
90 # instead of get_value() directly.
91
92 value = self.server.table.find_value(version, pkgarch, checksum, history)
93 upstream_max = await self.upstream_client.max_package_pr(version, pkgarch)
94
95 if value is not None:
96
97 # The configuration is already known locally.
98
99 if history:
100 value = self.server.table.get_value(version, pkgarch, checksum, history)
101 else:
102 existing_value = value
103 # In "no history", we need to make sure the value doesn't decrease
104 # and is at least greater than the maximum upstream value
105 # and the maximum local value
106
107 local_max = self.server.table.find_package_max_value(version, pkgarch)
108 if revision_smaller(value, local_max):
109 value = increase_revision(local_max)
110
111 if revision_smaller(value, upstream_max):
112 # Ask upstream whether it knows the checksum
113 upstream_value = await self.upstream_client.test_pr(version, pkgarch, checksum)
114 if upstream_value is None:
115 # Upstream doesn't have our checksum, let create a new one
116 value = upstream_max + ".0"
117 else:
118 # Fine to take the same value as upstream
119 value = upstream_max
120
121 if not value == existing_value and not self.server.read_only:
122 self.server.table.store_value(version, pkgarch, checksum, value)
123
124 return {"value": value}
125
126 # The configuration is a new one for the local server
127 # Let's ask the upstream server whether it knows it
128
129 known_upstream = await self.upstream_client.test_package(version, pkgarch)
130
131 if not known_upstream:
132
133 # The package is not known upstream, must be a local-only package
134 # Let's compute the PR number using the local-only method
135
136 value = self.server.table.get_value(version, pkgarch, checksum, history)
137 return {"value": value}
138
139 # The package is known upstream, let's ask the upstream server
140 # whether it knows our new output hash
141
142 value = await self.upstream_client.test_pr(version, pkgarch, checksum)
143
144 if value is not None:
145
146 # Upstream knows this output hash, let's store it and use it too.
147
148 if not self.server.read_only:
149 self.server.table.store_value(version, pkgarch, checksum, value)
150 # If the local server is read only, won't be able to store the new
151 # value in the database and will have to keep asking the upstream server
152 return {"value": value}
153
154 # The output hash doesn't exist upstream, get the most recent number from upstream (x)
155 # Then, we want to have a new PR value for the local server: x.y
156
157 upstream_max = await self.upstream_client.max_package_pr(version, pkgarch)
158 # Here we know that the package is known upstream, so upstream_max can't be None
159 subvalue = self.server.table.find_new_subvalue(version, pkgarch, upstream_max)
160
161 if not self.server.read_only:
162 self.server.table.store_value(version, pkgarch, checksum, subvalue)
163
164 return {"value": subvalue}
165
166 async def process_requests(self):
167 if self.server.upstream is not None:
168 self.upstream_client = await create_async_client(self.server.upstream)
169 else:
170 self.upstream_client = None
171
172 try:
173 await super().process_requests()
174 finally:
175 if self.upstream_client is not None:
176 await self.upstream_client.close()
87 177
88 async def handle_import_one(self, request): 178 async def handle_import_one(self, request):
89 response = None 179 response = None
@@ -92,8 +182,9 @@ class PRServerClient(bb.asyncrpc.AsyncServerConnection):
92 pkgarch = request["pkgarch"] 182 pkgarch = request["pkgarch"]
93 checksum = request["checksum"] 183 checksum = request["checksum"]
94 value = request["value"] 184 value = request["value"]
185 history = request["history"]
95 186
96 value = self.server.table.importone(version, pkgarch, checksum, value) 187 value = self.server.table.importone(version, pkgarch, checksum, value, history)
97 if value is not None: 188 if value is not None:
98 response = {"value": value} 189 response = {"value": value}
99 190
@@ -104,9 +195,10 @@ class PRServerClient(bb.asyncrpc.AsyncServerConnection):
104 pkgarch = request["pkgarch"] 195 pkgarch = request["pkgarch"]
105 checksum = request["checksum"] 196 checksum = request["checksum"]
106 colinfo = request["colinfo"] 197 colinfo = request["colinfo"]
198 history = request["history"]
107 199
108 try: 200 try:
109 (metainfo, datainfo) = self.server.table.export(version, pkgarch, checksum, colinfo) 201 (metainfo, datainfo) = self.server.table.export(version, pkgarch, checksum, colinfo, history)
110 except sqlite3.Error as exc: 202 except sqlite3.Error as exc:
111 self.logger.error(str(exc)) 203 self.logger.error(str(exc))
112 metainfo = datainfo = None 204 metainfo = datainfo = None
@@ -117,11 +209,12 @@ class PRServerClient(bb.asyncrpc.AsyncServerConnection):
117 return {"readonly": self.server.read_only} 209 return {"readonly": self.server.read_only}
118 210
119class PRServer(bb.asyncrpc.AsyncServer): 211class PRServer(bb.asyncrpc.AsyncServer):
120 def __init__(self, dbfile, read_only=False): 212 def __init__(self, dbfile, read_only=False, upstream=None):
121 super().__init__(logger) 213 super().__init__(logger)
122 self.dbfile = dbfile 214 self.dbfile = dbfile
123 self.table = None 215 self.table = None
124 self.read_only = read_only 216 self.read_only = read_only
217 self.upstream = upstream
125 218
126 def accept_client(self, socket): 219 def accept_client(self, socket):
127 return PRServerClient(socket, self) 220 return PRServerClient(socket, self)
@@ -134,6 +227,9 @@ class PRServer(bb.asyncrpc.AsyncServer):
134 self.logger.info("Started PRServer with DBfile: %s, Address: %s, PID: %s" % 227 self.logger.info("Started PRServer with DBfile: %s, Address: %s, PID: %s" %
135 (self.dbfile, self.address, str(os.getpid()))) 228 (self.dbfile, self.address, str(os.getpid())))
136 229
230 if self.upstream is not None:
231 self.logger.info("And upstream PRServer: %s " % (self.upstream))
232
137 return tasks 233 return tasks
138 234
139 async def stop(self): 235 async def stop(self):
@@ -147,14 +243,15 @@ class PRServer(bb.asyncrpc.AsyncServer):
147 self.table.sync() 243 self.table.sync()
148 244
149class PRServSingleton(object): 245class PRServSingleton(object):
150 def __init__(self, dbfile, logfile, host, port): 246 def __init__(self, dbfile, logfile, host, port, upstream):
151 self.dbfile = dbfile 247 self.dbfile = dbfile
152 self.logfile = logfile 248 self.logfile = logfile
153 self.host = host 249 self.host = host
154 self.port = port 250 self.port = port
251 self.upstream = upstream
155 252
156 def start(self): 253 def start(self):
157 self.prserv = PRServer(self.dbfile) 254 self.prserv = PRServer(self.dbfile, upstream=self.upstream)
158 self.prserv.start_tcp_server(socket.gethostbyname(self.host), self.port) 255 self.prserv.start_tcp_server(socket.gethostbyname(self.host), self.port)
159 self.process = self.prserv.serve_as_process(log_level=logging.WARNING) 256 self.process = self.prserv.serve_as_process(log_level=logging.WARNING)
160 257
@@ -233,7 +330,7 @@ def run_as_daemon(func, pidfile, logfile):
233 os.remove(pidfile) 330 os.remove(pidfile)
234 os._exit(0) 331 os._exit(0)
235 332
236def start_daemon(dbfile, host, port, logfile, read_only=False): 333def start_daemon(dbfile, host, port, logfile, read_only=False, upstream=None):
237 ip = socket.gethostbyname(host) 334 ip = socket.gethostbyname(host)
238 pidfile = PIDPREFIX % (ip, port) 335 pidfile = PIDPREFIX % (ip, port)
239 try: 336 try:
@@ -249,7 +346,7 @@ def start_daemon(dbfile, host, port, logfile, read_only=False):
249 346
250 dbfile = os.path.abspath(dbfile) 347 dbfile = os.path.abspath(dbfile)
251 def daemon_main(): 348 def daemon_main():
252 server = PRServer(dbfile, read_only=read_only) 349 server = PRServer(dbfile, read_only=read_only, upstream=upstream)
253 server.start_tcp_server(ip, port) 350 server.start_tcp_server(ip, port)
254 server.serve_forever() 351 server.serve_forever()
255 352
@@ -336,6 +433,9 @@ def auto_start(d):
336 433
337 host = host_params[0].strip().lower() 434 host = host_params[0].strip().lower()
338 port = int(host_params[1]) 435 port = int(host_params[1])
436
437 upstream = d.getVar("PRSERV_UPSTREAM") or None
438
339 if is_local_special(host, port): 439 if is_local_special(host, port):
340 import bb.utils 440 import bb.utils
341 cachedir = (d.getVar("PERSISTENT_DIR") or d.getVar("CACHE")) 441 cachedir = (d.getVar("PERSISTENT_DIR") or d.getVar("CACHE"))
@@ -350,7 +450,7 @@ def auto_start(d):
350 auto_shutdown() 450 auto_shutdown()
351 if not singleton: 451 if not singleton:
352 bb.utils.mkdirhier(cachedir) 452 bb.utils.mkdirhier(cachedir)
353 singleton = PRServSingleton(os.path.abspath(dbfile), os.path.abspath(logfile), host, port) 453 singleton = PRServSingleton(os.path.abspath(dbfile), os.path.abspath(logfile), host, port, upstream)
354 singleton.start() 454 singleton.start()
355 if singleton: 455 if singleton:
356 host = singleton.host 456 host = singleton.host