5 Script to make sure the conditions database interface is behaving as expected.
7 We do this by creating a local http server which acts as a mock database for
8 getting the payload information and payloads and then we run through different scenarios:
13 - corrupt payload information
14 - incomplete payload information
15 - correct payload information
16 - payload file missing
17 - payload file checksum mismatch
23 from http.server
import HTTPServer, BaseHTTPRequestHandler
24 from urllib.parse
import urlparse, parse_qs
25 from b2test_utils
import clean_working_directory, safe_process, skip_test, configure_logging_for_tests
26 import multiprocessing
31 """Simple ConditionsDB server which handles just the two things needed to
32 test the interface: get a list of payloads for the current run and download
33 a payloadfile. It will return different payloads for the experiments to
34 check for different error conditions"""
37 globaltag_states =
"""[
39 { "name": "TESTING" },
40 { "name": "VALIDATED" },
41 { "name": "PUBLISHED" },
42 { "name": "RUNNING" },
47 example_payload =
"""[{{
49 "baseUrl": "%(baseurl)s",
51 "checksum":"{checksum}",
52 "revision":{revision},
53 "payloadUrl":"dbstore_BeamParameters_rev_{revision}.root",
54 "basf2Module": {{ "name":"BeamParameters", "basf2Package": {{ "name":"dbstore" }} }}
68 "1":
'[{ "foo": { } }]',
72 "3": example_payload.format(checksum=
"2447fbcf76419fbbc7c6d015ef507769", revision=
"1"),
74 "4": example_payload.format(checksum=
"00[wrong checksum]", revision=
"1"),
76 "5": example_payload.format(checksum=
"00[missing]", revision=
"2"),
78 "6": example_payload.format(checksum=
"2447fbcf76419fbbc7c6d015ef507769", revision=
"2")[:-1] +
"," +
79 example_payload.format(checksum=
"2447fbcf76419fbbc7c6d015ef507769", revision=
"1")[1:-1] +
"," +
80 example_payload.format(checksum=
"2447fbcf76419fbbc7c6d015ef507769", revision=
"3")[1:],
85 "localtest":
"PUBLISHED",
87 "invalidgt":
"INVALID",
91 """Return a given xml string"""
92 self.send_response(200)
94 self.wfile.write(xml.encode())
96 def log_message(self, format, *args):
97 """Override default logging to remove timestamp"""
98 print(
"MockConditionsDB:", format % args)
100 def log_error(self, *args):
101 """Disable error logs"""
105 """Parse a get request"""
106 url = urlparse(self.path)
107 params = parse_qs(url.query)
108 if url.path ==
"/v2/globalTagStatus":
109 return self.reply(self.globaltag_states)
111 elif url.path.startswith(
"/v2/globalTag"):
113 gtname = url.path.split(
"/")[-1]
114 if gtname
in self.globaltags:
115 return self.reply(
'{ "name": "%s", "globalTagStatus": { "name": "%s" } }' % (gtname, self.globaltags[gtname]))
117 return self.send_error(404)
119 if url.path.endswith(
"/iovPayloads/"):
120 exp = params[
"expNumber"][0]
121 run = params[
"runNumber"][0]
124 self.send_error(int(exp))
130 if exp
in self.payloads:
131 baseurl =
"http://%s:%s" % self.server.socket.getsockname()
132 return self.reply(self.payloads[exp] % dict(exp=exp, run=run, baseurl=baseurl))
135 filename = os.path.basename(url.path)
137 filename = filename.replace(
"rev_3",
"rev_1")
138 basedir = basf2.find_file(
"framework/tests/conditions_testpayloads")
139 path = os.path.join(basedir, filename)
140 if os.path.isfile(path):
142 self.send_response(200)
144 with open(path,
"rb")
as f:
145 shutil.copyfileobj(f, self.wfile)
152 def run_mockdb(pipe):
153 """Startup the mock conditions db server and send the port we listen on back
154 to the parent process"""
159 httpd = HTTPServer((
"127.0.0.1", 12701), SimpleConditionsDB)
162 skip_test(
"Socket 12701 is in use, cannot continue")
165 port = httpd.socket.getsockname()[1]
169 httpd.serve_forever()
172 def run_redirect(pipe, redir_port):
173 """Startup the redirection server: any request should be transparently forwarded
174 to the other using 308 http replies"""
176 class SimpleRedirectServer(BaseHTTPRequestHandler):
178 self.send_response(308)
179 self.send_header(
"Location", f
"http://127.0.0.1:{redir_port}{self.path}")
182 def log_message(self, format, *args):
183 """Override default logging to remove timestamp"""
184 print(
"Redirect Server:", format % args)
186 def log_error(self, *args):
187 """Disable error logs"""
191 httpd = HTTPServer((
"127.0.0.1", 12702), SimpleRedirectServer)
195 skip_test(
"Socket 12702 is in use, cannot continue")
199 httpd.serve_forever()
202 def dbprocess(host, path, lastChangeCallback=lambda: None, *, globaltag=
"localtest"):
203 """Process a given path in a child process so that FATAL will not abort this
204 script but just the child and configure to use a central database at the given host"""
206 basf2.reset_database()
208 with clean_working_directory():
210 configure_logging_for_tests()
211 basf2.logging.log_level = basf2.LogLevel.DEBUG
212 basf2.logging.debug_level = 30
213 basf2.conditions.reset()
214 basf2.conditions.expert_settings(download_cache_location=
"db-cache")
215 basf2.conditions.override_globaltags([globaltag])
217 basf2.conditions.metadata_providers = [host]
218 basf2.conditions.payload_locations = []
224 basf2.conditions.expert_settings(backoff_factor=1, connection_timeout=5,
225 stalled_timeout=5, max_retries=3)
228 basf2.set_random_seed(
"something important")
230 conn = multiprocessing.Pipe(
False)
233 mock_conditionsdb = multiprocessing.Process(target=run_mockdb, args=(conn[1],))
234 mock_conditionsdb.daemon =
True
235 mock_conditionsdb.start()
237 mock_port = conn[0].recv()
239 if mock_port
is None:
243 redir_server = multiprocessing.Process(target=run_redirect, args=(conn[1], mock_port))
244 redir_server.daemon =
True
246 redir_port = conn[0].recv()
247 if redir_port
is None:
251 mock_host = f
"http://localhost:{mock_port}/"
252 redir_host = f
"http://localhost:{redir_port}/"
257 evtinfo = main.add_module(
"EventInfoSetter")
258 main.add_module(
"PrintBeamParameters")
262 for exp
in range(len(SimpleConditionsDB.payloads) + 1):
264 basf2.B2INFO(f
">>> check exp {exp}: {SimpleConditionsDB.payloads[str(exp)][0:20]}...)")
266 basf2.B2INFO(f
">>> check exp {exp}")
267 evtinfo.param({
"expList": [exp, exp, exp],
"runList": [0, 1, 2],
"evtNumList": [1, 1, 1]})
268 dbprocess(mock_host, main)
270 dbprocess(redir_host, main)
272 basf2.B2INFO(
">>> check that a invalid global tag or a misspelled global tag actually throw errors")
273 evtinfo.param({
"expList": [3],
"runList": [0],
"evtNumList": [1]})
274 for gt
in [
"newgt",
"invalidgt",
"horriblymisspelled",
275 "h͌̉e̳̞̞͆ͨ̏͋̕ ͍͚̱̰̀͡c͟o͛҉̟̰̫͔̟̪̠m̴̀ͯ̿͌ͨ̃͆e̡̦̦͖̳͉̗ͨͬ̑͌̃ͅt̰̝͈͚͍̳͇͌h̭̜̙̦̣̓̌̃̓̀̉͜!̱̞̻̈̿̒̀͢!̋̽̍̈͐ͫ͏̠̹̺̜̬͍ͅ"]:
276 dbprocess(mock_host, main, globaltag=gt)
278 basf2.B2INFO(
">>> check retry on 503 errors")
279 evtinfo.param({
"expList": [503],
"runList": [0],
"evtNumList": [1]})
280 dbprocess(mock_host, main)
281 basf2.B2INFO(
">>> check again without retries")
282 basf2.conditions.expert_settings(max_retries=0)
283 dbprocess(mock_host, main)
286 evtinfo.param({
"expList": [0],
"runList": [0],
"evtNumList": [1]})
288 basf2.B2INFO(
">>> try to open localhost on port 0, this should always be refused")
289 dbprocess(
"http://localhost:0", main)
291 basf2.B2INFO(
">>> and once more with a non existing host name to check for lookup errors")
292 dbprocess(
"http://nosuchurl/", main)
294 basf2.B2INFO(
">>> and once more with a non existing protocol")
295 dbprocess(
"nosuchproto://nosuchurl/", main)
297 basf2.B2INFO(
">>> and once more with a totally bogus url")
298 dbprocess(
"h͌̉e̳̞̞͆ͨ̏͋̕ ͍͚̱̰̀͡c͟o͛҉̟̰̫͔̟̪̠m̴̀ͯ̿͌ͨ̃͆e̡̦̦͖̳͉̗ͨͬ̑͌̃ͅt̰̝͈͚͍̳͇͌h̭̜̙̦̣̓̌̃̓̀̉͜!̱̞̻̈̿̒̀͢!̋̽̍̈͐ͫ͏̠̹̺̜̬͍ͅ", main)
300 basf2.B2INFO(
""">>> try to have a list of servers from environment variable
301 We expect that it fails over to the third server, {mock_host}, but then succeeds
303 evtinfo.param({
"expList": [3],
"runList": [0],
"evtNumList": [1]})
305 "http://localhost:0",
306 "http://h͌̉e̳̞̞͆ͨ̏͋̕c͟o͛҉̟̰̫͔̟̪̠m̴̀ͯ̿͌ͨ̃͆e̡̦̦͖̳͉̗ͨͬ̑͌̃ͅt̰̝͈͚͍̳͇͌h̭̜̙̦̣̓̌̃̓̀̉͜!̱̞̻̈̿̒̀͢",
309 os.environ[
"BELLE2_CONDB_SERVERLIST"] =
" ".join(serverlist)
313 basf2.B2INFO(
""">>> try to have a list of servers from steering file
314 We expect that it fails over to the third server, {mock_host}, but then succeeds
316 dbprocess(
"", main, lastChangeCallback=
lambda: basf2.set_central_serverlist(serverlist))
318 if "ssl" in sys.argv:
322 for hostname
in (
"expired",
"wrong.host",
"self-signed",
"untrusted-root"):
323 dbprocess(f
"https://{hostname}.badssl.com/", main)