15 Python interface to the ConditionsDB
19 from basf2
import B2FATAL, B2ERROR, B2INFO, B2WARNING
21 from requests.auth
import HTTPBasicAuth, HTTPDigestAuth
22 from requests.packages.urllib3.fields
import RequestField
23 from requests.packages.urllib3.filepost
import encode_multipart_formdata
26 from versioning
import upload_global_tag, jira_global_tag_v2
27 from collections
import defaultdict
28 from concurrent.futures
import ThreadPoolExecutor, wait
as futures_wait
33 def encode_name(name):
34 """Escape name to be used in an url"""
35 return urllib.parse.quote(name, safe=
"")
38 def file_checksum(filename):
39 """Calculate md5 hash of file"""
40 md5hash = hashlib.md5()
41 with open(filename,
"rb")
as data:
42 md5hash.update(data.read())
43 return md5hash.hexdigest()
46 def chunks(container, chunk_size):
47 """Cut a container in chunks of max. chunk_size"""
50 chunk = tuple(itertools.islice(it, chunk_size))
57 """Small container class to help compare payload information for efficient
58 comparison between globaltags"""
62 """Set all internal members from the json information of the payload and the iov.
65 payload (dict): json information of the payload as returned by REST api
66 iov (dict): json information of the iov as returned by REST api
69 iov = {
"payloadIovId":
None,
"expStart":
None,
"runStart":
None,
"expEnd":
None,
"runEnd":
None}
73 payload[
'basf2Module'][
'name'],
76 payload[
'payloadUrl'],
79 (iov[
"expStart"], iov[
"runStart"], iov[
"expEnd"], iov[
"runEnd"]),
82 def __init__(self, payload_id, name, revision, checksum, payload_url, base_url, iov_id=None, iov=None):
84 Create a new object from the given information
105 """Return the full url to the payload on the server"""
109 """Make object hashable"""
113 """Check if two payloads are equal"""
114 return (self.
namename, self.
checksumchecksum, self.
ioviov) == (other.name, other.checksum, other.iov)
117 """Sort payloads by name, iov, revision"""
118 return (self.
namename.lower(), self.
ioviov, self.
revisionrevision) < (other.name.lower(), other.iov, other.revision)
121 """return a human readable name for the IoV"""
122 if self.
ioviov
is None:
125 if self.
ioviov == (0, 0, -1, -1):
128 e1, r1, e2, r2 = self.
ioviov
130 if r1 == 0
and r2 == -1:
133 return f
"exp {e1}, runs {r1}+"
135 return f
"exp {e1}, run {r1}"
137 return f
"exp {e1}, runs {r1} - {r2}"
139 if e2 == -1
and r1 == 0:
140 return f
"exp {e1} - forever"
142 return f
"exp {e1}, run {r1} - forever"
143 elif r1 == 0
and r2 == -1:
144 return f
"exp {e1}-{e2}, all runs"
146 return f
"exp {e1}, run {r1} - exp {e2}, all runs"
148 return f
"exp {e1}, run {r1} - exp {e2}, run {r2}"
152 """Class to interface conditions db REST interface"""
155 BASE_URLS = [
"http://belle2db.sdcc.bnl.gov/b2s/rest/"]
158 """Class to be thrown by request() if there is any error"""
163 """Resolve the list of server urls. If a url is given just return it.
164 Otherwise return servers listed in BELLE2_CONDB_SERVERLIST or the
168 given_url (str): Explicit base_url. If this is not None it will be
169 returned as is in a list of length 1
172 a list of urls to try for database connectivity
175 base_url_list = ConditionsDB.BASE_URLS[:]
176 base_url_env = os.environ.get(
"BELLE2_CONDB_SERVERLIST",
None)
177 if given_url
is not None:
178 base_url_list = [given_url]
179 elif base_url_env
is not None:
180 base_url_list = base_url_env.split()
181 B2INFO(
"Getting Conditions Database servers from Environment:")
182 for i, url
in enumerate(base_url_list, 1):
183 B2INFO(f
" {i}. {url}")
186 for url
in base_url_list:
187 if url.startswith(
"http://"):
188 full_list.append(
"https" + url[4:])
190 full_list.append(url)
193 def __init__(self, base_url=None, max_connections=10, retries=3):
195 Create a new instance of the interface
198 base_url (string): base url of the rest interface
199 max_connections (int): number of connections to keep open, mostly useful for threaded applications
200 retries (int): number of retries in case of connection problems
206 adapter = requests.adapters.HTTPAdapter(
207 pool_connections=max_connections, pool_maxsize=max_connections,
208 max_retries=retries, pool_block=
True
210 self.
_session_session.mount(
"http://", adapter)
211 self.
_session_session.mount(
"https://", adapter)
213 if "BELLE2_CONDB_PROXY" in os.environ:
215 "http": os.environ.get(
"BELLE2_CONDB_PROXY"),
216 "https": os.environ.get(
"BELLE2_CONDB_PROXY"),
219 base_url_list = ConditionsDB.get_base_urls(base_url)
221 for url
in base_url_list:
226 req.raise_for_status()
227 except requests.RequestException
as e:
228 B2WARNING(f
"Problem connecting to {url}:\n {e}\n Trying next server ...")
232 B2FATAL(
"No working database servers configured, giving up")
238 self.
_session_session.headers.update({
"Accept":
"application/json",
"Cache-Control":
"no-cache"})
242 Set authentication credentials when talking to the database
246 password (str): password
247 basic (bool): if True us HTTP Basic authentication, otherwise HTTP Digest
249 authtype = HTTPBasicAuth
if basic
else HTTPDigestAuth
250 self.
_session_session.auth = authtype(user, password)
252 def request(self, method, url, message=None, *args, **argk):
254 Request function, similar to requests.request but adding the base_url
257 method (str): GET, POST, etc.
258 url (str): url for the request, base_url will be prepended
259 message (str): message to show when starting the request and if it fails
261 All other arguments will be forwarded to requests.request.
263 if message
is not None:
268 except requests.exceptions.ConnectionError
as e:
269 B2FATAL(
"Could not access '" + self.
_base_url_base_url + url.lstrip(
"/") +
"': " + str(e))
271 if req.status_code >= 300:
275 response = req.json()
276 message = response.get(
"message",
"")
277 colon =
": " if message.strip()
else ""
278 error =
"Request {method} {url} returned {code} {reason}{colon}{message}".format(
279 method=method, url=url,
280 code=response[
"code"],
281 reason=response[
"reason"],
285 except json.JSONDecodeError:
287 error =
"Request {method} {url} returned non JSON response {code}: {content}".format(
288 method=method, url=url,
289 code=req.status_code,
293 if message
is not None:
298 if method !=
"HEAD" and req.status_code != requests.codes.no_content:
301 except json.JSONDecodeError
as e:
302 B2INFO(f
"Invalid response: {req.content}")
304 .format(e, method=method, url=url))
308 """Get a list of all globaltags. Returns a dictionary with the globaltag
309 names and the corresponding ids in the database"""
312 req = self.
requestrequest(
"GET",
"/globalTags")
314 B2ERROR(f
"Could not get the list of globaltags: {e}")
318 for tag
in req.json():
319 result[tag[
"name"]] = tag
324 """Check whether the globaltag with the given name exists."""
327 self.
requestrequest(
"GET",
"/globalTag/{globalTagName}".format(globalTagName=encode_name(name)))
334 """Get the id of the globaltag with the given name. Returns either the
335 id or None if the tag was not found"""
338 req = self.
requestrequest(
"GET",
"/globalTag/{globalTagName}".format(globalTagName=encode_name(name)))
340 B2ERROR(f
"Cannot find globaltag '{name}': {e}")
347 Get the dictionary describing the given globaltag type (currently
348 one of DEV or RELEASE). Returns None if tag type was not found.
351 req = self.
requestrequest(
"GET",
"/globalTagType")
353 B2ERROR(f
"Could not get list of valid globaltag types: {e}")
356 types = {e[
"name"]: e
for e
in req.json()}
361 B2ERROR(
"Unknown globaltag type: '{}', please use one of {}".format(name,
", ".join(types)))
366 Create a new globaltag
368 info = {
"name": name,
"description": description,
"modifiedBy": user,
"isDefault":
False}
370 req = self.
requestrequest(
"POST",
"/globalTag/DEV", f
"Creating globaltag {name}", json=info)
372 B2ERROR(f
"Could not create globaltag {name}: {e}")
379 Return list of all payloads in the given globaltag where each element is
380 a `PayloadInformation` instance
383 gobalTag (str): name of the globaltag
384 exp (int): if given limit the list of payloads to the ones valid for
385 the given exp,run combination
386 run (int): if given limit the list of payloads to the ones valid for
387 the given exp,run combination
388 message (str): additional message to show when downloading the
389 payload information. Will be directly appended to
390 "Obtaining lists of iovs for globaltag {globalTag}"
393 Both, exp and run, need to be given at the same time. Just supplying
394 an experiment or a run number will not work
396 globalTag = encode_name(globalTag)
400 msg = f
"Obtaining list of iovs for globaltag {globalTag}, exp={exp}, run={run}{message}"
401 req = self.
requestrequest(
"GET",
"/iovPayloads", msg, params={
'gtName': globalTag,
'expNumber': exp,
'runNumber': run})
403 msg = f
"Obtaining list of iovs for globaltag {globalTag}{message}"
404 req = self.
requestrequest(
"GET", f
"/globalTag/{globalTag}/globalTagPayloads", msg)
406 for item
in req.json():
407 payload = item[
"payload" if 'payload' in item
else "payloadId"]
408 if "payloadIov" in item:
409 iovs = [item[
'payloadIov']]
411 iovs = item[
'payloadIovs']
414 all_iovs.append(PayloadInformation.from_json(payload, iov))
421 Get a list of all defined payloads (for the given global_tag or by default for all).
422 Returns a dictionary which maps (module, checksum) to the payload id.
427 req = self.
requestrequest(
"GET",
"/globalTag/{global_tag}/payloads"
428 .format(global_tag=encode_name(global_tag)))
430 req = self.
requestrequest(
"GET",
"/payloads")
432 B2ERROR(f
"Cannot get list of payloads: {e}")
436 for payload
in req.json():
437 module = payload[
"basf2Module"][
"name"]
438 checksum = payload[
"checksum"]
439 result[(module, checksum)] = payload[
"payloadId"]
445 Check for the existence of payloads in the database.
448 payloads (list((str,str))): A list of payloads to check for. Each
449 payload needs to be a tuple of the name of the payload and the
450 md5 checksum of the payload file.
451 information (str): The information to be extracted from the
455 A dictionary with the payload identifiers (name, checksum) as keys
456 and the requested information as values for all payloads which are already
457 present in the database.
460 search_query = [{
"name": e[0],
"checksum": e[1]}
for e
in payloads]
462 req = self.
requestrequest(
"POST",
"/checkPayloads", json=search_query)
464 B2ERROR(f
"Cannot check for existing payloads: {e}")
468 for payload
in req.json():
469 module = payload[
"basf2Module"][
"name"]
470 checksum = payload[
"checksum"]
471 result[(module, checksum)] = payload[information]
477 Get the revision numbers of payloads in the database.
480 entries (list): A list of payload entries.
481 Each entry must have the attributes module and checksum.
487 result = self.
check_payloadscheck_payloads([(entry.module, entry.checksum)
for entry
in entries],
"revision")
491 for entry
in entries:
492 entry.revision = result.get((entry.module, entry.checksum), 0)
501 module (str): name of the module
502 filename (str): name of the file
503 checksum (str): md5 hexdigest of the file. Will be calculated automatically if not given
506 checksum = file_checksum(filename)
512 (filename, open(filename,
"rb").read(),
"application/x-root"),
513 (
"json", json.dumps({
"checksum": checksum,
"isDefault":
False}),
"application/json"),
518 for name, contents, mimetype
in files:
519 rf = RequestField(name=name, data=contents)
520 rf.make_multipart(content_type=mimetype)
523 post_body, content_type = encode_multipart_formdata(fields)
524 content_type =
''.join((
'multipart/mixed',) + content_type.partition(
';')[1:])
525 headers = {
'Content-Type': content_type}
530 req = self.
requestrequest(
"POST",
"/package/dbstore/module/{moduleName}/payload"
531 .format(moduleName=encode_name(module)),
532 data=post_body, headers=headers)
534 B2ERROR(f
"Could not create Payload: {e}")
537 return req.json()[
"payloadId"]
539 def create_iov(self, globalTagId, payloadId, firstExp, firstRun, finalExp, finalRun):
544 globalTagId (int): id of the globaltag, obtain with get_globalTagId()
545 payloadId (int): id of the payload, obtain from create_payload() or get_payloads()
546 firstExp (int): first experiment for which this iov is valid
547 firstRun (int): first run for which this iov is valid
548 finalExp (int): final experiment for which this iov is valid
549 finalRun (int): final run for which this iov is valid
552 payloadIovId of the created iov, None if creation was not successful
557 local_variables = locals()
558 variables = {e: int(local_variables[e])
for e
in
559 [
"globalTagId",
"payloadId",
"firstExp",
"firstRun",
"finalExp",
"finalRun"]}
561 B2ERROR(
"create_iov: All parameters need to be integers")
566 req = self.
requestrequest(
"POST",
"/globalTagPayload/{globalTagId},{payloadId}"
567 "/payloadIov/{firstExp},{firstRun},{finalExp},{finalRun}".format(**variables))
569 B2ERROR(f
"Could not create IOV: {e}")
572 return req.json()[
"payloadIovId"]
574 def get_iovs(self, globalTagName, payloadName=None):
575 """Return existing iovs for a given tag name. It returns a dictionary
576 which maps (payloadId, first runId, final runId) to iovId
579 globalTagName(str): Global tag name.
580 payloadName(str): Payload name (if None, selection by name is
585 req = self.
requestrequest(
"GET",
"/globalTag/{globalTagName}/globalTagPayloads"
586 .format(globalTagName=encode_name(globalTagName)))
592 for payload
in req.json():
593 payloadId = payload[
"payloadId"][
"payloadId"]
594 if payloadName
is not None:
595 if payload[
"payloadId"][
"basf2Module"][
"name"] != payloadName:
597 for iov
in payload[
"payloadIovs"]:
598 iovId = iov[
"payloadIovId"]
599 firstExp, firstRun = iov[
"expStart"], iov[
"runStart"]
600 finalExp, finalRun = iov[
"expEnd"], iov[
"runEnd"]
601 result[(payloadId, firstExp, firstRun, finalExp, finalRun)] = iovId
605 def upload(self, filename, global_tag, normalize=False, ignore_existing=False, nprocess=1, uploaded_entries=None):
607 Upload a testing payload storage to the conditions database.
610 filename (str): filename of the testing payload storage file that should be uploaded
611 global_tage (str): name of the globaltag to which the data should be uploaded
612 normalize (bool/str): if True the payload root files will be normalized to have the same checksum for the same content,
613 if normalize is a string in addition the file name in the root file metadata will be set to it
614 ignore_existing (bool): if True do not upload payloads that already exist
615 nprocess (int): maximal number of parallel uploads
616 uploaded_entries (list): the list of successfully uploaded entries
619 True if the upload was successful
624 B2INFO(f
"Reading payload list from {filename}")
625 entries = parse_testing_payloads_file(filename)
627 B2ERROR(f
"Problems with testing payload storage file {filename}, exiting")
631 B2INFO(f
"No payloads found in {filename}, exiting")
634 B2INFO(f
"Found {len(entries)} iovs to upload")
640 tagId = tagId[
"globalTagId"]
644 entries = sorted(set(reversed(entries)))
647 name = normalize
if normalize
is not True else None
649 e.normalize(name=name)
653 payloads = defaultdict(list)
655 payloads[(e.module, e.checksum)].append(e)
657 existing_payloads = {}
660 def upload_payload(item):
661 """Upload a payload file if necessary but first check list of existing payloads"""
663 if key
in existing_payloads:
664 B2INFO(f
"{key[0]} (md5:{key[1]}) already existing in database, skipping.")
665 payload_id = existing_payloads[key]
668 payload_id = self.
create_payloadcreate_payload(entry.module, entry.filename, entry.checksum)
669 if payload_id
is None:
672 B2INFO(f
"Created new payload {payload_id} for {entry.module} (md5:{entry.checksum})")
674 for entry
in entries:
675 entry.payload = payload_id
680 """Create an iov if necessary but first check the list of existing iovs"""
681 if entry.payload
is None:
684 iov_key = (entry.payload,) + entry.iov_tuple
685 if iov_key
in existing_iovs:
686 entry.iov = existing_iovs[iov_key]
687 B2INFO(f
"IoV {entry.iov_tuple} for {entry.module} (md5:{entry.checksum}) already existing in database, skipping.")
689 entry.payloadIovId = self.
create_iovcreate_iov(tagId, entry.payload, *entry.iov_tuple)
690 if entry.payloadIovId
is None:
693 B2INFO(f
"Created IoV {entry.iov_tuple} for {entry.module} (md5:{entry.checksum})")
698 with ThreadPoolExecutor(max_workers=nprocess)
as pool:
701 if not ignore_existing:
702 B2INFO(
"Downloading information about existing payloads and iovs...")
705 existing_payloads = {}
707 def create_future(iter, func, callback=None):
708 fn = pool.submit(iter, func)
709 if callback
is not None:
710 fn.add_done_callback(callback)
713 def update_iovs(iovs):
714 existing_iovs.update(iovs.result())
715 B2INFO(f
"Found {len(existing_iovs)} existing iovs in {global_tag}")
717 def update_payloads(payloads):
718 existing_payloads.update(payloads.result())
719 B2INFO(f
"Found {len(existing_payloads)} existing payloads")
721 create_future(self.
get_iovsget_iovs, global_tag, update_iovs)
723 for chunk
in chunks(payloads.keys(), 1000):
724 create_future(self.
check_payloadscheck_payloads, chunk, update_payloads)
726 futures_wait(futures)
729 failed_payloads = sum(0
if result
else 1
for result
in pool.map(upload_payload, payloads.items()))
730 if failed_payloads > 0:
731 B2ERROR(f
"{failed_payloads} payloads could not be uploaded")
735 for entry
in pool.map(create_iov, entries):
737 if uploaded_entries
is not None:
738 uploaded_entries.append(entry)
742 B2ERROR(f
"{failed_iovs} IoVs could not be created")
745 if uploaded_entries
is not None:
748 return failed_payloads + failed_iovs == 0
752 Upload a testing payload storage to a staging globaltag and create or update a jira issue
755 filename (str): filename of the testing payload storage file that should be uploaded
756 normalize (bool/str): if True the payload root files will be
757 normalized to have the same checksum for the same content, if
758 normalize is a string in addition the file name in the root file
759 metadata will be set to it
760 data (dict): a dictionary with the information provided by the user:
762 * task: category of globaltag, either main, online, prompt, data, mc, or analysis
763 * tag: the globaltage name
764 * request: type of request, either Update, New, or Modification. The latter two imply task == main because
765 if new payload classes are introduced or payload classes are modified then they will first be included in
766 the main globaltag. Here a synchronization of code and payload changes has to be managed.
767 If new or modified payload classes should be included in other globaltags they must already be in a release.
768 * pull-request: number of the pull request containing new or modified payload classes,
769 only for request == New or Modified
770 * backward-compatibility: description of what happens if the old payload is encountered by the updated code,
771 only for request == Modified
772 * forward-compatibility: description of what happens if a new payload is encountered by the existing code,
773 only for request == Modified
774 * release: the required release version
775 * reason: the reason for the request
776 * description: a detailed description for the globaltag manager
777 * issue: identifier of an existing jira issue (optional)
778 * user: name of the user
779 * time: time stamp of the request
781 password: the password for access to jira or the access token and secret for oauth access
784 True if the upload and jira issue creation/upload was successful
788 data[
'tag'] = upload_global_tag(data[
'task'])
789 if data[
'tag']
is None:
790 data[
'tag'] = f
"staging_{data['task']}_{data['user']}_{data['time']}"
794 if not self.
create_globalTagcreate_globalTag(data[
'tag'], data[
'reason'], data[
'user']):
798 B2INFO(f
"Uploading testing database {filename} to globaltag {data['tag']}")
800 if not self.
uploadupload(filename, data[
'tag'], normalize, uploaded_entries=entries):
805 issue = data[
'issue']
807 issue = jira_global_tag_v2(data[
'task'])
809 issue = {
"components": [{
"name":
"globaltag"}]}
812 if type(issue)
is tuple:
813 description = issue[1].format(**data)
817 |*Upload globaltag* | {data['tag']} |
818 |*Request reason* | {data['reason']} |
819 |*Required release* | {data['release']} |
820 |*Type of request* | {data['request']} |
822 if 'pull-request' in data.keys():
823 description += f
"|*Pull request* | \\#{data['pull-request']} |\n"
824 if 'backward-compatibility' in data.keys():
825 description += f
"|*Backward compatibility* | \\#{data['backward-compatibility']} |\n"
826 if 'forward-compatibility' in data.keys():
827 description += f
"|*Forward compatibility* | \\#{data['forward-compatibility']} |\n"
828 description +=
'|*Details* |' +
''.join(data[
'details']) +
' |\n'
829 if data[
'task'] ==
'online':
830 description +=
'|*Impact on data taking*|' +
''.join(data[
'data_taking']) +
' |\n'
833 description +=
'\nPayloads\n||Name||Revision||IoV||\n'
834 for entry
in entries:
835 description += f
"|{entry.module} | {entry.revision} | ({entry.iov_str()}) |\n"
838 if type(issue)
is dict:
839 issue[
"description"] = description
840 if "summary" in issue.keys():
841 issue[
"summary"] = issue[
"summary"].format(**data)
843 issue[
"summary"] = f
"Globaltag request for {data['task']} by {data['user']} at {data['time']}"
844 if "project" not in issue.keys():
845 issue[
"project"] = {
"key":
"BII"}
846 if "issuetype" not in issue.keys():
847 issue[
"issuetype"] = {
"name":
"Task"}
848 if data[
"task"] ==
"main":
849 issue[
"labels"] = [
"TUPPR"]
851 B2INFO(f
"Creating jira issue for {data['task']} globaltag request")
852 if isinstance(password, str):
853 response = requests.post(
'https://agira.desy.de/rest/api/latest/issue', auth=(data[
'user'], password),
854 json={
'fields': issue})
856 fields = {
'issue': json.dumps(issue)}
857 if 'user' in data.keys():
858 fields[
'user'] = data[
'user']
860 fields[
'token'] = password[0]
861 fields[
'secret'] = password[1]
862 response = requests.post(
'https://b2-master.belle2.org/cgi-bin/jira_issue.py', data=fields)
863 if response.status_code
in range(200, 210):
864 B2INFO(f
"Issue successfully created: https://agira.desy.de/browse/{response.json()['key']}")
866 B2ERROR(
'The creation of the issue failed: ' + requests.status_codes._codes[response.status_code][0])
873 new_issue_config = jira_global_tag_v2(data[
'task'])
874 if isinstance(new_issue_config, dict)
and "assignee" in new_issue_config:
875 user = new_issue_config[
'assignee'].get(
'name',
None)
876 if user
is not None and isinstance(password, str):
877 response = requests.post(f
'https://agira.desy.de/rest/api/latest/issue/{issue}/watchers',
878 auth=(data[
'user'], password), json=user)
879 if response.status_code
in range(200, 210):
880 B2INFO(f
"Added {user} as watcher to {issue}")
882 B2WARNING(f
"Could not add {user} as watcher to {issue}: {response.status_code}")
884 B2INFO(f
"Commenting on jira issue {issue} for {data['task']} globaltag request")
885 if isinstance(password, str):
886 response = requests.post(
'https://agira.desy.de/rest/api/latest/issue/%s/comment' % issue,
887 auth=(data[
'user'], password), json={
'body': description})
889 fields = {
'id': issue,
'user': user,
'comment': description}
891 fields[
'token'] = password[0]
892 fields[
'secret'] = password[1]
893 response = requests.post(
'https://b2-master.belle2.org/cgi-bin/jira_issue.py', data=fields)
894 if response.status_code
in range(200, 210):
895 B2INFO(f
"Issue successfully updated: https://agira.desy.de/browse/{issue}")
897 B2ERROR(
'The commenting of the issue failed: ' + requests.status_codes._codes[response.status_code][0])
903 def require_database_for_test(timeout=60, base_url=None):
904 """Make sure that the database is available and skip the test if not.
906 This function should be called in test scripts if they are expected to fail
907 if the database is down. It either returns when the database is ok or it
908 will signal test_basf2 that the test should be skipped and exit
911 if os.environ.get(
"BELLE2_CONDB_GLOBALTAG",
None) ==
"":
912 raise Exception(
"Access to the Database is disabled")
913 base_url_list = ConditionsDB.get_base_urls(base_url)
914 for url
in base_url_list:
916 req = requests.request(
"HEAD", url.rstrip(
'/') +
"/v2/globalTags")
917 req.raise_for_status()
918 except requests.RequestException
as e:
919 B2WARNING(f
"Problem connecting to {url}:\n {e}\n Trying next server ...")
923 print(
"TEST SKIPPED: No working database servers configured, giving up", file=sys.stderr)
927 def enable_debugging():
928 """Enable verbose output of python-requests to be able to debug http connections"""
932 import http.client
as http_client
934 http_client.HTTPConnection.debuglevel = 1
936 logging.basicConfig()
937 logging.getLogger().setLevel(logging.DEBUG)
938 requests_log = logging.getLogger(
"requests.packages.urllib3")
939 requests_log.setLevel(logging.DEBUG)
940 requests_log.propagate =
True
def get_payloads(self, global_tag=None)
def get_iovs(self, globalTagName, payloadName=None)
def upload(self, filename, global_tag, normalize=False, ignore_existing=False, nprocess=1, uploaded_entries=None)
def get_globalTagType(self, name)
def check_payloads(self, payloads, information="payloadId")
def has_globalTag(self, name)
def set_authentication(self, user, password, basic=True)
def create_iov(self, globalTagId, payloadId, firstExp, firstRun, finalExp, finalRun)
def request(self, method, url, message=None, *args, **argk)
def __init__(self, base_url=None, max_connections=10, retries=3)
def get_revisions(self, entries)
def create_globalTag(self, name, description, user)
def get_globalTagInfo(self, name)
_session
session object to get keep-alive support and connection pooling
def get_all_iovs(self, globalTag, exp=None, run=None, message=None)
def get_base_urls(given_url)
def staging_request(self, filename, normalize, data, password)
def create_payload(self, module, filename, checksum=None)
_base_url
base url to be prepended to all requests