12This module contains some classes to check the possibility and calculate the
13necessary updates for a running globaltag
17from collections
import defaultdict
18from basf2
import B2ERROR, B2WARNING, B2INFO
19from conditions_db
import ConditionsDB
24 """Define the different modes for the update of a running tag"""
45 Errors raised when trying to update the running globaltag. Can have extra
46 variables in `extra_vars` to be shown to the user
for additional information
58 Calculate and apply the necessary changes to update a running globaltag
60 For this we take two globaltags: the running one
and a staging one
61 containing all the payloads
and iovs to be added to the running tag. We then
63 1. Make sure they are
in the correct states (RUNNING
and VALIDATED)
64 2. Make sure all payloads
in the running tag start
and end (
except for open
65 iovs) before the given ``valid_from`` run
66 3. Make sure the staging tag
is overlap
and gap free
67 4. Make all payloads
in staging start at either 0,0
or on/after the given
69 5. Make sure all payloads
in staging are unbound unless the mode
is
70 ``ALLOW_CLOSED``
or ``FIX_CLOSED``. In case of ``FIX_CLOSED`` extend the
72 6. Close all payloads to be updated
in the running tag that are open just
73 before the validity
in the staging tag.
76 def __init__(self, db, running, staging, valid_from, mode, dry_run=False):
77 """Initialize the class
80 db (ConditionsDB): reference to the database object
81 running (str): name of the running tag
82 staging (str): name of the staging tag
83 valid_from (tuple(int,int)): first valid exp,run
84 mode (RunningTagUpdateMode): the mode of the update
85 dry_run (bool): If true only check, don't do anything.
86 But be more lenient with globaltag state of staging.
93 valid_from = tuple(map(int, valid_from))
94 if len(valid_from) != 2:
95 raise ValueError(
"exp,run number needs to have two elements")
96 except Exception
as e:
109 self.
_allow_closed = mode
in (RunningTagUpdateMode.ALLOW_CLOSED, RunningTagUpdateMode.FIX_CLOSED,
110 RunningTagUpdateMode.FULL_REPLACEMENT)
136 """Check the state of a globaltag given the tag information object returned by the database
138 1) that it's found and
139 2) that it has the same state as in ``required``
142 tagname: name of the tag
for error messages
143 taginfo: tag information returned
from the database,
None if the tag could
not be found
144 required: required state
for the tag.
147 an `RunningTagUpdaterError`
if any condition
is not fulfilled
151 state = taginfo[
'globalTagStatus'][
'name'].upper()
152 if state != required.upper():
154 B2WARNING(f
"Globaltag '{tagname}' not in {required.upper()} state, continuing to display changes")
159 """Run all necessary checks on all globaltags"""
163 if self.
_mode == RunningTagUpdateMode.SIMPLE:
172 Check that all payloads in the running tag start
and end (
or are open)
173 before the first valid run
for the update
176 "payloads start after first valid run": 0,
177 "payloads end after first valid run": 0
179 earliest_valid_from = (0, 0)
184 B2ERROR(f
"Payload in running tag '{tagname}' starts after first valid run",
185 payload=p.name, iov=p.iov, **{
"first valid run": self.
_valid_from})
186 errors[
"payloads start after first valid run"] += 1
188 elif iov.final != IntervalOfValidity.always().final
and iov.final >= self.
_valid_from:
189 B2ERROR(f
"Payload in running tag '{tagname}' ends after first valid run",
190 payload=p.name, iov=p.iov, **{
"first valid run": self.
_valid_from})
191 errors[
"payloads end after first valid run"] += 1
193 earliest_valid_from = max(earliest_valid_from, iov.first)
194 if iov.final != IntervalOfValidity.always().final:
195 earliest_valid_from = max(earliest_valid_from, iov.final)
198 B2INFO(
"Earliest possible update of the running tag would be exp "
199 f
"{earliest_valid_from[0]}, run {earliest_valid_from[1] + 1}")
202 if any(errors.values()):
204 f
"running tag '{tagname}'", **errors)
208 Extra simple case where we want to have a very simple staging tag just
209 consisting of (0,0,-1,-1) iovs, one per payload
213 payload_names = set()
214 errors = {
"duplicate payloads": 0,
"wrong validity": 0}
216 if p.name
in payload_names:
217 B2ERROR(f
"Duplicate payload in staging tag '{tagname}'", name=p.name)
218 errors[
"duplicate payloads"] += 1
219 payload_names.add(p.name)
220 if p.iov != (0, 0, -1, -1):
221 errors[
"wrong validity"] += 1
222 B2ERROR(f
"Wrong validity for payload in staging tag '{tagname}'", name=p.name, validity=p.iov)
228 if any(errors.values()):
232 always =
IoVSet([IntervalOfValidity.always()])
237 Check if the staging tag
is
240 3. all payloads are open (unless the mode allows closed payloads)
241 4. all payloads start at 0,0
or after the first valid run
for the update
244 tagname (str): Name of the globaltag
for error messages
249 explicit_coverage = defaultdict(IoVSet)
252 full_coverage = defaultdict(IoVSet)
256 errors = {
'overlaps': 0,
'gaps': 0,
'starts too early': 0,
'closed payloads': 0}
263 full_coverage[p.name].add(iov, allow_overlaps=
False)
264 except ValueError
as e:
265 B2ERROR(f
"Overlap in globaltag '{tagname}'", payload=p.name, overlap=e)
266 errors[
'overlaps'] += 1
269 full_coverage[p.name].add(iov, allow_overlaps=
True)
272 if iov.first != (0, 0):
273 explicit_coverage[p.name].add(iov, allow_overlaps=
True)
277 prev = latest_iov.get(p.name,
None)
279 latest_iov[p.name] = p
281 latest_iov[p.name] = max(p, prev)
292 for name, iovs
in full_coverage.items():
294 B2ERROR(f
"Gap in globaltag '{tagname}'", payload=name, gaps=iovs.gaps)
295 errors[
'gaps'] += len(iovs) - 1
298 if iovs.final != IntervalOfValidity.always().final:
302 errors[
'closed payloads'] += 1
303 log_func(f
"Payload in globaltag '{tagname}' not open ended",
304 payload=name, **{
"final run": iovs.final})
307 for name, iovs
in explicit_coverage.items():
309 B2ERROR(f
"Payload in globaltag '{tagname}' starts before the given first valid run",
310 payload=name, **{
"actual start validity": iovs.first,
312 errors[
'starts too early'] += 1
317 for payload
in latest_iov.values():
318 if payload.iov[2:] != (-1, -1):
319 B2INFO(
"Extending closed iov to infinity", name=payload.name,
320 **{
"old iov": payload.iov})
321 payload.iov = payload.iov[:2] + (-1, -1)
322 full_coverage[payload.name].add(payload.iov, allow_overlaps=
True)
325 if any(errors.values()):
334 Calculate the operations needed to merge staging into the running base tag
346 if p.iov[2:] == (-1, -1):
349 if not self.
_mode == RunningTagUpdateMode.FULL_REPLACEMENT:
351 staging_range = valid_range
359 if first_iov
is not None and first_iov.revision == p.revision:
363 if not staging_range:
368 operations.append([
"CLOSE", p])
375 operations.append([
"CREATE", p])
382 """Apply a previously calculated update to the globaltag
385 This action cannot be undone, only call it after checking the
386 operations returned by the calculation of the update
397 operations.append({
"operation":
"MODIFY",
"data": [payload.iov_id] + list(payload.iov[2:])})
399 operations.append({
"operation":
"CREATE",
"data": [payload.payload_id] + list(payload.iov)})
408 self.
_db.request(
"POST", f
"/globalTagPayload/{tag}/updateRunningPayloads",
409 f
"updating running tag {tag}", json=operations)
extra_vars
extra keyword arguments given to the exception constructor
def __init__(self, description, **extra_vars)
Initialize the class.
_running_payloads
Payloads currently in the running tag.
def _check_staging_tag(self, tagname, payloads)
_valid_from
First valid run for the update.
_mode
True if we want to allow payloads in the staging tag to be closed, for example when retiring a payloa...
_staging_payloads
Payloads currently in the staging tag.
_staging_info
Globaltag information for the staging tag.
def _check_running_tag(self, tagname, payloads)
_staging_coverage
Dictionary mapping payload names in the staging tag to the coverage they have in the staging tag,...
_staging_first_iovs
First iov per payload name in staging to not close and open the same revision.
_allow_closed
Do we allow closed iovs in staging?
_running_info
Globaltag information for the running tag.
_fix_closed
Do we want to automatically open closed iovs?
def __init__(self, db, running, staging, valid_from, mode, dry_run=False)
def _check_state(self, tagname, taginfo, required)
_operations
Operations for the update, filled by calculate_update()
_dry_run
If we're in dry run mode be less critical about globaltag states (just show warnings) but refuse to d...
def calculate_update(self)
def _check_staging_tag_simple(self, tagname, payloads)
_db
Reference to the database object to use for queries.