Belle II Software development
testing_payloads.py
1#!/usr/bin/env python3
2
3
10
11"""
12Script to upload local database to ConditionsDB.
13
14This script takes a local database file and will upload all payloads defined in
15this file to the ConditionsDB and create iovs for each payload. It assumes that
16all tags already exist.
17"""
18
19import os
20from basf2 import B2ERROR, LogLevel, logging
21from conditions_db import file_checksum
22from B2Tools.b2root import normalize_file
23
24
26 """Class to keep information about an entry in the testing payloads storage file"""
27
28 def __init__(self, line, basedir):
29 """Create new entry from line in testing payloads storage file"""
30 try:
31 name, revision, iov = line.split()
32 except ValueError:
33 raise ValueError("line must be of the form 'dbstore/<payloadname> <revision> "
34 "<firstExp>,<firstRun>,<finalExp>,<finalRun>'")
35
36
37 self.revision = revision
38
39 try:
40
41 self.module = name.split("/")[1]
42 except IndexError:
43 raise ValueError("payload name must be of the form dbstore/<payloadname>")
44
45 try:
46 iov = [int(e) for e in iov.split(",")]
47 except ValueError:
48 raise ValueError("experiment and run numbers must be integers")
49
50 if len(iov) != 4:
51 raise ValueError("IoV needs to be four values (firstExp,firstRun,finalExp,finalRun)")
52
53
54 self.filename = os.path.join(basedir, f"dbstore_{self.module}_rev_{self.revision}.root")
55
56 self.firstRun = {"exp": iov[0], "run": iov[1]}
57
58 self.finalRun = {"exp": iov[2], "run": iov[3]}
59
60 self.__checksum = None
61
62 self.__id = (self.module,) + tuple(iov)
63
64 self.payload = None
65
66 self.iov = None
67
68 def normalize(self, name=None, root_version=61408):
69 """Normalize the root file to have the same checksum for the same content"""
70 normalize_file(self.filename, in_place=True, name=name, root_version=root_version)
71 self.__checksum = file_checksum(self.filename)
72
73 @property
74 def checksum(self):
75 """Return checksum, calculated on first access"""
76 if self.__checksum is None:
77 self.__checksum = file_checksum(self.filename)
78 return self.__checksum
79
80 def __repr__(self):
81 """Convert to useful string representation"""
82 return repr(self.__id + (self.filename,))
83
84 def __eq__(self, other):
85 """Compare to other entries, only consider package, module and iov for equality"""
86 return self.__id == other.__id
87
88 def __le__(self, other):
89 """Compare to other entries, only consider package, module and iov for equality"""
90 return self.__id <= other.__id
91
92 def __lt__(self, other):
93 """Compare to other entries, only consider package, module and iov for equality"""
94 return self.__id < other.__id
95
96 def __hash__(self):
97 """Provide hash function to be able to create a set"""
98 return hash(self.__id)
99
100 @property
101 def iov_tuple(self):
102 """Return a tuple with the valid exp,run range"""
103 return self.firstRun['exp'], self.firstRun['run'], self.finalRun['exp'], self.finalRun['run']
104
105 def iov_str(self):
106 """String representation of IoV"""
107 return f"{self.firstRun['exp']}/{self.firstRun['run']} - {self.finalRun['exp']}/{self.finalRun['run']}"
108
109
110def parse_testing_payloads_file(filename, check_existing=True):
111 """
112 Parse a testing payload storage file
113
114 This is the python equivalent of TestingPayloadStorage::read implemented in
115 python. Each line in in the file should be of the form
116
117 "dbstore/{payloadname} {revision} {firstExp},{firstRun},{finalExp},{finalRun}"
118
119 Comments can be started using "#", everything including this character to
120 the end of the line will be ignored.
121
122 Parameters:
123 filename (str): filename of the testing payload storage file to parse
124 check_existing (bool): if True check if the payloads actually exist where
125 they should be
126
127 Returns:
128 a list of TestingPayloadEntry objects or None if there were any errors
129 """
130
131 # make sure the testing payload storage file exists
132 if not os.path.exists(filename):
133 B2ERROR("Given database file does not exist")
134 return None
135
136 # set the directory
137 payload_dir = os.path.dirname(filename)
138
139 # remember the list of errors before start of the function to see if we
140 # create any new ones
141 old_error_count = logging.log_stats[LogLevel.ERROR]
142 entries = []
143 with open(filename) as dbfile:
144 for lineno, line in enumerate(dbfile, 1):
145 # ignore comments
146 line = line.split("#", 1)[0].strip()
147 # and empty lines
148 if not line:
149 continue
150 # parse the line
151 try:
152 entry = TestingPayloadEntry(line, payload_dir)
153 except ValueError as e:
154 B2ERROR(f"{filename}:{lineno} error: {e}")
155 continue
156
157 if check_existing and not os.path.exists(entry.filename):
158 B2ERROR(f"{filename}:{lineno} error: cannot find payload file {entry.filename}")
159 continue
160
161 entries.append(entry)
162
163 # ok, if we had any errors so far, exit
164 if logging.log_stats[LogLevel.ERROR] > old_error_count:
165 return None
166
167 return entries
def normalize(self, name=None, root_version=61408)
__checksum
variable for checksum, calculated on first access
__id
object to uniquely identify this entry (payload + iov)