12 This module contains some classes to check the possibility and calculate the
13 necessary updates for a running globaltag
17 from collections
import defaultdict
18 from basf2
import B2ERROR, B2WARNING, B2INFO
19 from 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
57 Calculate and apply the necessary changes to update a running globaltag
59 For this we take two globaltags: the running one and a staging one
60 containing all the payloads and iovs to be added to the running tag. We then
62 1. Make sure they are in the correct states (RUNNING and VALIDATED)
63 2. Make sure all payloads in the running tag start and end (except for open
64 iovs) before the given ``valid_from`` run
65 3. Make sure the staging tag is overlap and gap free
66 4. Make all payloads in staging start at either 0,0 or on/after the given
68 5. Make sure all payloads in staging are unbound unless the mode is
69 ``ALLOW_CLOSED`` or ``FIX_CLOSED``. In case of ``FIX_CLOSED`` extend the
71 6. Close all payloads to be updated in the running tag that are open just
72 before the validity in the staging tag.
74 def __init__(self, db, running, staging, valid_from, mode, dry_run=False):
75 """Initialize the class
78 db (ConditionsDB): reference to the database object
79 running (str): name of the running tag
80 stagin (str): name of the staging tag
81 valid_from (tuple(int,int)): first valid exp,run
82 mode (RunningTagUpdateMode): the mode of the update
83 dry_run (bool): If true only check, don't do anything.
84 But be more lenient with globaltag state of staging.
91 valid_from = tuple(map(int, valid_from))
92 if len(valid_from) != 2:
93 raise ValueError(
"exp,run number needs to have two elements")
94 except Exception
as e:
107 self.
_allow_closed_allow_closed = mode
in (RunningTagUpdateMode.ALLOW_CLOSED, RunningTagUpdateMode.FIX_CLOSED,
108 RunningTagUpdateMode.FULL_REPLACEMENT)
110 self.
_fix_closed_fix_closed = (mode == RunningTagUpdateMode.FIX_CLOSED)
134 """Check the state of a globaltag given the tag information object returned by the database
136 1) that it's found and
137 2) that it has the same state as in ``required``
140 tagname: name of the tag for error messages
141 taginfo: tag information returned from the database, None if the tag could not be found
142 required: required state for the tag.
145 an `RunningTagUpdaterError` if any condition is not fulfilled
149 state = taginfo[
'globalTagStatus'][
'name'].upper()
150 if state != required.upper():
152 B2WARNING(f
"Globaltag '{tagname}' not in {required.upper()} state, continuing to display changes")
157 """Run all necessary checks on all globaltags"""
161 if self.
_mode_mode == RunningTagUpdateMode.SIMPLE:
170 Check that all payloads in the running tag start and end (or are open)
171 before the first valid run for the update
174 "payloads start after first valid run": 0,
175 "payloads end after first valid run": 0
177 earliest_valid_from = (0, 0)
182 B2ERROR(f
"Payload in running tag '{tagname}' starts after first valid run",
183 payload=p.name, iov=p.iov, **{
"first valid run": self.
_valid_from_valid_from})
184 errors[
"payloads start after first valid run"] += 1
186 elif iov.final != IntervalOfValidity.always().final
and iov.final >= self.
_valid_from_valid_from:
187 B2ERROR(f
"Payload in running tag '{tagname}' ends after first valid run",
188 payload=p.name, iov=p.iov, **{
"first valid run": self.
_valid_from_valid_from})
189 errors[
"payloads end after first valid run"] += 1
191 earliest_valid_from = max(earliest_valid_from, iov.first)
192 if iov.final != IntervalOfValidity.always().final:
193 earliest_valid_from = max(earliest_valid_from, iov.final)
196 B2INFO(
"Earliest possible update of the running tag would be exp "
197 f
"{earliest_valid_from[0]}, run {earliest_valid_from[1] + 1}")
200 if any(errors.values()):
202 f
"running tag '{tagname}'", **errors)
206 Extra simple case where we want to have a very simple staging tag just
207 consisting of (0,0,-1,-1) iovs, one per payload
211 payload_names = set()
212 errors = {
"duplicate payloads": 0,
"wrong validity": 0}
214 if p.name
in payload_names:
215 B2ERROR(f
"Duplicate payload in staging tag '{tagname}'", name=p.name)
216 errors[
"duplicate payloads"] += 1
217 payload_names.add(p.name)
218 if p.iov != (0, 0, -1, -1):
219 errors[
"wrong validity"] += 1
220 B2ERROR(f
"Wrong validity for payload in staging tag '{tagname}'", name=p.name, validity=p.iov)
226 if any(errors.values()):
230 always =
IoVSet([IntervalOfValidity.always()])
231 self.
_staging_coverage_staging_coverage = {name: always
for name
in payload_names}
235 Check if the staging tag is
238 3. all payloads are open (unless the mode allows closed payloads)
239 4. all payloads start at 0,0 or after the first valid run for the update
242 tagname (str): Name of the globaltag for error messages
243 payloads (list(conditions_db.PayloadInformation)): List of payloads in the tag
247 explicit_coverage = defaultdict(IoVSet)
250 full_coverage = defaultdict(IoVSet)
254 errors = {
'overlaps': 0,
'gaps': 0,
'starts too early': 0,
'closed payloads': 0}
261 full_coverage[p.name].add(iov, allow_overlaps=
False)
262 except ValueError
as e:
263 B2ERROR(f
"Overlap in globaltag '{tagname}'", payload=p.name, overlap=e)
264 errors[
'overlaps'] += 1
267 full_coverage[p.name].add(iov, allow_overlaps=
True)
270 if iov.first != (0, 0):
271 explicit_coverage[p.name].add(iov, allow_overlaps=
True)
275 prev = latest_iov.get(p.name,
None)
277 latest_iov[p.name] = p
279 latest_iov[p.name] = max(p, prev)
290 for name, iovs
in full_coverage.items():
292 B2ERROR(f
"Gap in globaltag '{tagname}'", payload=name, gaps=iovs.gaps)
293 errors[
'gaps'] += len(iovs) - 1
296 if iovs.final != IntervalOfValidity.always().final:
300 errors[
'closed payloads'] += 1
301 log_func(f
"Payload in globaltag '{tagname}' not open ended",
302 payload=name, **{
"final run": iovs.final})
305 for name, iovs
in explicit_coverage.items():
307 B2ERROR(f
"Payload in globaltag '{tagname}' starts before the given first valid run",
308 payload=name, **{
"actual start validity": iovs.first,
309 "expected start validity": self.
_valid_from_valid_from})
310 errors[
'starts too early'] += 1
315 for payload
in latest_iov.values():
316 if payload.iov[2:] != (-1, -1):
317 B2INFO(
"Extending closed iov to infinity", name=payload.name,
318 **{
"old iov": payload.iov})
319 payload.iov = payload.iov[:2] + (-1, -1)
320 full_coverage[payload.name].add(payload.iov, allow_overlaps=
True)
323 if any(errors.values()):
332 Calculate the operations needed to merge staging into the running base tag
344 if p.iov[2:] == (-1, -1):
347 if not self.
_mode_mode == RunningTagUpdateMode.FULL_REPLACEMENT:
349 staging_range = valid_range
357 if first_iov
is not None and first_iov.revision == p.revision:
361 if not staging_range:
366 operations.append([
"CLOSE", p])
373 operations.append([
"CREATE", p])
380 """Apply a previously calculated update to the globaltag
383 This action cannot be undone, only call it after checking the
384 operations returned by the calculation of the update
395 operations.append({
"operation":
"MODIFY",
"data": [payload.iov_id] + list(payload.iov[2:])})
397 operations.append({
"operation":
"CREATE",
"data": [payload.payload_id] + list(payload.iov)})
406 self.
_db_db.request(
"POST", f
"/globalTagPayload/{tag}/updateRunningPayloads",
407 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 retireing a paylo...
_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.