4 This module contains some classes to check the possibility and calculate the
5 necessary updates for a running globaltag
9 from collections
import defaultdict
10 from basf2
import B2ERROR, B2WARNING, B2INFO
11 from .
import ConditionsDB
12 from .iov
import IntervalOfValidity, IoVSet
16 """Define the different modes for the update of a running tag"""
37 Errors raised when trying to update the running globaltag. Can have extra
38 variables in `extra_vars` to be shown to the user for additional information
41 def __init__(self, description, **extra_vars):
42 super().__init__(description)
49 Calculate and apply the necessary changes to update a running globaltag
51 For this we take two globaltags: the running one and a staging one
52 containing all the payloads and iovs to be added to the running tag. We then
54 1. Make sure they are in the correct states (RUNNING and VALIDATED)
55 2. Make sure all payloads in the running tag start and end (except for open
56 iovs) before the given ``valid_from`` run
57 3. Make sure the staging tag is overlap and gap free
58 4. Make all payloads in staging start at either 0,0 or on/after the given
60 5. Make sure all payloads in staging are unbound unless the mode is
61 ``ALLOW_CLOSED`` or ``FIX_CLOSED``. In case of ``FIX_CLOSED`` extend the
63 6. Close all payloads to be updated in the running tag that are open just
64 before the validity in the staging tag.
67 def __init__(self, db, running, staging, valid_from, mode):
73 valid_from = tuple(map(int, valid_from))
74 if len(valid_from) != 2:
75 raise ValueError(
"exp,run number needs to have two elements")
76 except Exception
as e:
86 self.
_allow_closed = mode
in (RunningTagUpdateMode.ALLOW_CLOSED, RunningTagUpdateMode.FIX_CLOSED,
87 RunningTagUpdateMode.FULL_REPLACEMENT)
113 """Check the state of a globaltag given the tag information object returned by the database
115 1) that it's found and
116 2) that it has the same state as in ``required``
119 tagname: name of the tag for error messages
120 taginfo: tag information returned from the database, None if the tag could not be found
121 required: required state for the tag.
124 an `RunningTagUpdaterError` if any condition is not fulfilled
128 state = taginfo[
'globalTagStatus'][
'name'].upper()
129 if state != required.upper():
133 """Run all necessary checks on all globaltags"""
137 if self.
_mode == RunningTagUpdateMode.SIMPLE:
146 Check that all payloads in the running tag start and end (or are open)
147 before the first valid run for the update
150 "payloads start after first valid run": 0,
151 "payloads end after first valid run": 0
157 B2ERROR(f
"Payload in running tag '{tagname}' starts after first valid run",
158 payload=p.name, iov=p.iov, **{
"first valid run": self.
_valid_from})
159 errors[
"payloads start after first valid run"] += 1
161 elif iov.final != IntervalOfValidity.always().final
and iov.final >= self.
_valid_from:
162 B2ERROR(f
"Payload in running tag '{tagname}' ends after first valid run",
163 payload=p.name, iov=p.iov, **{
"first valid run": self.
_valid_from})
164 errors[
"payloads end after first valid run"] += 1
167 if any(errors.values()):
169 f
"running tag '{tagname}'", **errors)
173 Extra simple case where we want to have a very simple staging tag just
174 consisting of (0,0,-1,-1) iovs, one per payload
178 payload_names = set()
179 errors = {
"duplicate payloads": 0,
"wrong validity": 0}
181 if p.name
in payload_names:
182 B2ERROR(f
"Duplicate payload in staging tag '{tagname}'", name=p.name)
183 errors[
"duplicate payloads"] += 1
184 payload_names.add(p.name)
185 if p.iov != (0, 0, -1, -1):
186 errors[
"wrong validity"] += 1
187 B2ERROR(f
"Wrong validity for payload in staging tag '{tagname}'", name=p.name, validity=p.iov)
193 if any(errors.values()):
197 always =
IoVSet([IntervalOfValidity.always()])
202 Check if the staging tag is
205 3. all payloads are open (unless the mode allows closed payloads)
206 4. all payloads start at 0,0 or after the first valid run for the update
209 tagname (str): Name of the globaltag for error messages
210 payloads (list(conditions_db.PayloadInformation)): List of payloads in the tag
214 explicit_coverage = defaultdict(IoVSet)
217 full_coverage = defaultdict(IoVSet)
221 errors = {
'overlaps': 0,
'gaps': 0,
'starts too early': 0,
'closed payloads': 0}
228 full_coverage[p.name].add(iov, allow_overlaps=
False)
229 except ValueError
as e:
230 B2ERROR(f
"Overlap in globaltag '{tagname}'", payload=p.name, overlap=e)
231 errors[
'overlaps'] += 1
234 full_coverage[p.name].add(iov, allow_overlaps=
True)
237 if iov.first != (0, 0):
238 explicit_coverage[p.name].add(iov, allow_overlaps=
True)
242 prev = latest_iov.get(p.name,
None)
244 latest_iov[p.name] = p
246 latest_iov[p.name] = max(p, prev)
257 for name, iovs
in full_coverage.items():
259 B2ERROR(f
"Gap in globaltag '{tagname}'", payload=name, gaps=iovs.gaps)
260 errors[
'gaps'] += len(iovs) - 1
263 if iovs.final != IntervalOfValidity.always().final:
267 errors[
'closed payloads'] += 1
268 log_func(f
"Payload in globaltag '{tagname}' not open ended",
269 payload=name, **{
"final run": iovs.final})
272 for name, iovs
in explicit_coverage.items():
274 B2ERROR(f
"Payload in globaltag '{tagname}' starts before the given first valid run",
275 payload=name, **{
"actual start validity": iovs.first,
277 errors[
'starts too early'] += 1
282 for payload
in latest_iov.values():
283 if payload.iov[2:] != (-1, -1):
284 B2INFO(
"Extending closed iov to infinity", name=payload.name,
285 **{
"old iov": payload.iov})
286 payload.iov = payload.iov[:2] + (-1, -1)
287 full_coverage[payload.name].add(payload.iov, allow_overlaps=
True)
290 if any(errors.values()):
299 Calculate the operations needed to merge staging into the running base tag
311 if p.iov[2:] == (-1, -1):
314 if not self.
_mode == RunningTagUpdateMode.FULL_REPLACEMENT:
316 staging_range = valid_range
324 if first_iov
is not None and first_iov.revision == p.revision:
328 if not staging_range:
333 operations.append([
"CLOSE", p])
340 operations.append([
"CREATE", p])
347 """Apply a previously calculated update to the globaltag
350 This action cannot be undone, only call it after checking the
351 operations returned by the calculation of the update
359 operations.append({
"operation":
"MODIFY",
"data": [payload.iov_id] + list(payload.iov[2:])})
361 operations.append({
"operation":
"CREATE",
"data": [payload.payload_id] + list(payload.iov)})
370 self.
_db.request(
"POST", f
"/globalTagPayload/{tag}/updateRunningPayloads",
371 f
"updating running tag {tag}", json=operations)