12 This script provides a command line interface to all the tasks related to the
13 :ref:`Conditions database <conditionsdb_overview>`: manage globaltags and iovs as well as upload new payloads
14 or download of existing payloads.
16 The usage of this program is similar to git: there are sub commands like for
17 example ``tag`` which groups all actions related to the management of global
18 tags. All the available commands are listed below.
42 from basf2
import B2ERROR, B2WARNING, B2INFO, LogLevel, LogInfo, logging, \
45 from terminal_utils
import Pager
46 from dateutil.parser
import parse
as parse_date
47 from getpass
import getuser
48 from conditions_db
import ConditionsDB, enable_debugging, encode_name, PayloadInformation
57 def escape_ctrl_chars(name):
58 """Remove ANSI control characters from strings"""
60 if not hasattr(escape_ctrl_chars,
"_regex"):
61 escape_ctrl_chars._regex = re.compile(
"[\x00-\x1f\x7f-\x9f]")
65 if match.group(0).isspace():
67 return "\\x{:02x}".format(ord(match.group(0)))
69 return escape_ctrl_chars._regex.sub(escape, name)
72 def command_tag(args, db=None):
74 List, show, create, modify or clone globaltags.
76 This command allows to list, show, create modify or clone globaltags in the
77 central database. If no other command is given it will list all tags as if
78 "%(prog)s show" was given.
84 command_tag_list(args, db)
87 def command_tag_list(args, db=None):
89 List all available globaltags.
91 This command allows to list all globaltags, optionally limiting the output
92 to ones matching a given search term. By default invalidated globaltags
93 will not be included in the list, to show them as well please add
94 --with-invalid as option. Alternatively one can use --only-published to show
95 only tags which have been published
97 If the --regex option is supplied the search term will be interpreted as a
98 python regular expression where the case is ignored.
101 tagfilter = ItemFilter(args)
104 args.add_argument(
"--detail", action=
"store_true", default=
False,
105 help=
"show detailed information for all tags instead "
107 group = args.add_mutually_exclusive_group()
108 group.add_argument(
"--with-invalid", action=
"store_true", default=
False,
109 help=
"if given also invalidated tags will be shown")
110 group.add_argument(
"--only-published", action=
"store_true", default=
False,
111 help=
"if given only published tags will be shown")
112 tagfilter.add_arguments(
"tags")
116 if not tagfilter.check_arguments():
119 req = db.request(
"GET",
"/globalTags", f
"Getting list of globaltags{tagfilter}")
123 for item
in req.json():
124 if not tagfilter.check(item[
"name"]):
126 tagstatus = item[
"globalTagStatus"][
"name"]
127 if not getattr(args,
"with_invalid",
False)
and tagstatus ==
"INVALID":
129 if getattr(args,
"only_published",
False)
and tagstatus !=
"PUBLISHED":
134 taglist.sort(key=
lambda x: x[
"name"])
138 with Pager(
"List of globaltags{}{}".format(tagfilter,
" (detailed)" if getattr(args,
"detail",
False)
else ""),
True):
140 if getattr(args,
"detail",
False):
141 print_globaltag(db, item)
146 escape_ctrl_chars(item.get(
"description",
"")),
147 item[
"globalTagType"][
"name"],
148 item[
"globalTagStatus"][
"name"],
149 item[
"payloadCount"],
152 if not getattr(args,
"detail",
False):
153 table.insert(0, [
"id",
"name",
"description",
"type",
"status",
"# payloads"])
154 pretty_print_table(table, [-10, 0,
"*", -10, -10, -10], min_flexible_width=32)
157 def print_globaltag(db, *tags):
158 """ Print detailed globaltag information for the given globaltags side by side"""
159 results = [[
"id"], [
"name"], [
"description"], [
"type"], [
"status"],
160 [
"# payloads"], [
"created"], [
"modified"], [
"modified by"]]
165 if isinstance(info, str):
167 req = db.request(
"GET",
"/globalTag/{}".format(encode_name(info)),
168 f
"Getting info for globaltag {info}")
169 except ConditionsDB.RequestError
as e:
177 created = parse_date(info[
"dtmIns"])
178 modified = parse_date(info[
"dtmMod"])
179 results[0].append(str(info[
"globalTagId"])),
180 results[1].append(info[
"name"]),
181 results[2].append(escape_ctrl_chars(info.get(
"description",
"")))
182 results[3].append(info[
"globalTagType"][
"name"])
183 results[4].append(info[
"globalTagStatus"][
"name"]),
184 results[5].append(info[
"payloadCount"]),
185 results[6].append(created.astimezone(tz=
None).strftime(
"%Y-%m-%d %H:%M:%S local time"))
186 results[7].append(modified.astimezone(tz=
None).strftime(
"%Y-%m-%d %H:%M:%S local time"))
187 results[8].append(escape_ctrl_chars(info[
"modifiedBy"]))
189 ntags = len(results[0]) - 1
191 pretty_print_table(results, [11] + [
'*'] * ntags,
True)
195 def change_state(db, tag, state, force=False):
196 """Change the state of a global tag
198 If the new state is not revertible then ask for confirmation
200 state = state.upper()
201 if state
in [
"INVALID",
"PUBLISHED"]
and not force:
202 name = input(f
"ATTENTION: Marking a tag as {state} cannot be undone.\n"
203 "If you are sure you want to continue it please enter the tag name again: ")
205 B2ERROR(
"Names don't match, aborting")
208 db.request(
"PUT", f
"/globalTag/{encode_name(tag)}/updateGlobalTagStatus/{state}",
209 f
"Changing globaltag state {tag} to {state}")
212 def command_tag_show(args, db=None):
214 Show details about globaltags
216 This command will show details for the given globaltags like name,
217 description and number of payloads.
225 args.add_argument(
"tag", metavar=
"TAGNAME", nargs=
"+", help=
"globaltags to show")
230 with Pager(
"globaltag Information",
True):
232 ntags += print_globaltag(db, tag)
235 return len(args.tag) - ntags
238 def command_tag_create(args, db=None):
240 Create a new globaltag
242 This command creates a new globaltag in the database with the given name
243 and description. The name can only contain alpha-numeric characters and the
248 args.add_argument(
"type", metavar=
"TYPE", help=
"type of the globaltag to create, usually one of DEV or RELEASE")
249 args.add_argument(
"tag", metavar=
"TAGNAME", help=
"name of the globaltag to create")
250 args.add_argument(
"description", metavar=
"DESCRIPTION", help=
"description of the globaltag")
251 args.add_argument(
"-u",
"--user", metavar=
"USER", help=
"username who created the tag. "
252 "If not given we will try to supply a useful default")
256 info = {
"name": args.tag,
"description": args.description,
"modifiedBy": args.user,
"isDefault":
False}
258 if args.user
is None:
259 info[
"modifiedBy"] = os.environ.get(
"BELLE2_USER", getuser())
261 typeinfo = db.get_globalTagType(args.type)
265 req = db.request(
"POST",
"/globalTag/{}".format(encode_name(typeinfo[
"name"])),
266 "Creating globaltag {name}".format(**info),
268 B2INFO(
"Successfully created globaltag {name} (id={globalTagId})".format(**req.json()))
271 def command_tag_modify(args, db=None):
273 Modify a globaltag by changing name or description
275 This command allows to change the name or description of an existing globaltag.
276 You can supply any combination of -n,-d,-t and only the given values will be changed
279 args.add_argument(
"tag", metavar=
"TAGNAME", help=
"globaltag to modify")
280 args.add_argument(
"-n",
"--name", help=
"new name")
281 args.add_argument(
"-d",
"--description", help=
"new description")
282 args.add_argument(
"-t",
"--type", help=
"new type of the globaltag")
283 args.add_argument(
"-u",
"--user", metavar=
"USER", help=
"username who created the tag. "
284 "If not given we will try to supply a useful default")
285 args.add_argument(
"-s",
"--state", help=
"new globaltag state, see the command ``tag state`` for details")
289 req = db.request(
"GET",
"/globalTag/{}".format(encode_name(args.tag)),
290 f
"Getting info for globaltag {args.tag}")
294 old_name = info[
"name"]
296 for key
in [
"name",
"description"]:
297 value = getattr(args, key)
298 if value
is not None and value != info[key]:
302 info[
"modifiedBy"] = os.environ.get(
"BELLE2_USER", os.getlogin())
if args.user
is None else args.user
304 if args.type
is not None:
306 typeinfo = db.get_globalTagType(args.type)
310 if info[
'gloalTagType'] != typeinfo:
311 info[
"globalTagType"] = typeinfo
316 db.request(
"PUT",
"/globalTag",
317 "Modifying globaltag {} (id={globalTagId})".format(old_name, **info),
320 if args.state
is not None:
321 name = args.name
if args.name
is not None else old_name
322 return change_state(db, name, args.state)
325 def command_tag_clone(args, db=None):
327 Clone a given globaltag including all IoVs
329 This command allows to clone a given globaltag with a new name but still
330 containing all the IoVs defined in the original globaltag.
334 args.add_argument(
"tag", metavar=
"TAGNAME", help=
"globaltag to be cloned")
335 args.add_argument(
"name", metavar=
"NEWNAME", help=
"name of the cloned globaltag")
339 req = db.request(
"GET",
"/globalTag/{}".format(encode_name(args.tag)),
340 f
"Getting info for globaltag {args.tag}")
344 req = db.request(
"POST",
"/globalTags/{globalTagId}".format(**info),
345 "Cloning globaltag {name} (id={globalTagId})".format(**info))
349 cloned_info = req.json()
350 cloned_info[
"name"] = args.name
352 db.request(
"PUT",
"/globalTag",
"Renaming globaltag {name} (id={globalTagId})".format(**cloned_info),
356 def command_tag_state(args, db):
358 Change the state of a globaltag.
360 This command changes the state of a globaltag to the given value.
362 Usually the valid states are
365 Tag can be modified, payloads and iovs can be created and deleted. This
366 is the default state for new or cloned globaltags and is not suitable
367 for use in data analysis
369 Can be transitioned to TESTING, RUNNING
372 Tag cannot be modified and is suitable for testing but can be reopened
374 Can be transitioned to VALIDATED, OPEN
377 Tag cannot be modified and has been tested.
379 Can be transitioned to PUBLISHED, OPEN
382 Tag cannot be modified and is suitable for user analysis
384 Can only be transitioned to INVALID
387 Tag can only be modified by adding new runs, not modifying the payloads
391 Tag is invalid and should not be used for anything.
393 This state is end of life for a globaltag and cannot be transitioned to
396 .. versionadded:: release-04-00-00
399 args.add_argument(
"tag", metavar=
"TAGNAME", help=
"globaltag to be changed")
400 args.add_argument(
"state", metavar=
"STATE", help=
"new state for the globaltag")
401 args.add_argument(
"--force", default=
False, action=
"store_true", help=argparse.SUPPRESS)
404 return change_state(db, args.tag, args.state, args.force)
407 def command_tag_publish(args, db):
411 This command sets the state of a globaltag to PUBLISHED. This will make the
412 tag immutable and no more modifications are possible. A confirmation dialog
415 .. deprecated:: release-04-00-00
416 Use ``tag state $name PUBLISHED`` instead
419 args.add_argument(
"tag", metavar=
"TAGNAME", help=
"globaltag to be published")
422 return change_state(db, args.tag,
"PUBLISHED")
425 def command_tag_invalidate(args, db):
427 Invalidate a globaltag.
429 This command sets the state of a globaltag to INVALID. This will disqualify
430 this tag from being used in user analysis. A confirmation dialog will be
433 .. deprecated:: release-04-00-00
434 Use ``tag state $name INVALID`` instead
437 args.add_argument(
"tag", metavar=
"TAGNAME", help=
"globaltag to be invalidated")
440 return change_state(db, args.tag,
"INVALID")
443 def remove_repeated_values(table, columns, keep=None):
444 """Strip repeated values from a table of values
446 This function takes a table (a list of lists with all the same length) and
447 removes values in certain columns if they are identical in consecutive rows.
449 It does this in a dependent way only if the previous columns are identical
450 will it continue stripping further columns. For example, given the table ::
459 If we want to remove duplicates in all columns in order it would look like this:
461 >>> remove_repeated_values(table, [0,1])
469 But we can give selected columns to strip one after the other
471 >>> remove_repeated_values(table, [1,0])
479 In addition, we might want to only strip some columns if previous columns
480 were identical but keep the values of the previous column. For this one can
483 >>> remove_repeated_values(table, [0,1,2], keep=[0])
492 table (list(list(str))): 2d table of values
493 columns (list(int)): indices of columns to consider in order
494 keep (set(int)): indices of columns to not strip
496 last_values = [
None] * len(columns)
497 for row
in table[1:]:
498 current_values = [row[i]
for i
in columns]
499 for i, curr, last
in zip(columns, current_values, last_values):
503 if keep
and i
in keep:
508 last_values = current_values
511 def command_diff(args, db):
512 """Compare two globaltags
514 This command allows to compare two globaltags. It will show the changes in
515 a format similar to a unified diff but by default it will not show any
516 context, only the new or removed payloads. Added payloads are marked with a
517 ``+`` in the first column, removed payloads with a ``-``
519 If ``--full`` is given it will show all payloads, even the ones common to
520 both globaltags. The differences can be limited to a given run and
521 limited to a set of payloads names using ``--filter`` or ``--exclude``. If
522 the ``--regex`` option is supplied the search term will be interpreted as a
523 python regular expression where the case is ignored.
525 .. versionchanged:: release-03-00-00
526 modified output structure and added ``--human-readable``
527 .. versionchanged:: after release-04-00-00
528 added parameter ``--checksums`` and ``--show-ids``
530 iovfilter = ItemFilter(args)
532 args.add_argument(
"--full", default=
False, action=
"store_true",
533 help=
"If given print all iovs, also those which are the same in both tags")
534 args.add_argument(
"--run", type=int, nargs=2, metavar=
"N", help=
"exp and run numbers "
535 "to limit showing iovs to a ones present in a given run")
536 args.add_argument(
"--human-readable", default=
False, action=
"store_true",
537 help=
"If given the iovs will be written in a more human friendly format. "
538 "Also repeated payload names will be omitted to create a more readable listing.")
539 args.add_argument(
"--checksums", default=
False, action=
"store_true",
540 help=
"If given don't show the revision number but the md5 checksum")
541 args.add_argument(
"--show-ids", default=
False, action=
"store_true",
542 help=
"If given also show the payload and iov ids for each iov")
544 args.add_argument(
"tagA", metavar=
"TAGNAME1", help=
"base for comparison")
545 args.add_argument(
"tagB", metavar=
"TAGNAME2", help=
"tagname to compare")
546 iovfilter.add_arguments(
"payloads")
550 if not iovfilter.check_arguments():
553 with Pager(f
"Differences between globaltags {args.tagA} and {args.tagB}{iovfilter}",
True):
554 print(
"globaltags to be compared:")
555 ntags = print_globaltag(db, args.tagA, args.tagB)
559 listA = [e
for e
in db.get_all_iovs(args.tagA, message=str(iovfilter))
if iovfilter.check(e.name)]
560 listB = [e
for e
in db.get_all_iovs(args.tagB, message=str(iovfilter))
if iovfilter.check(e.name)]
562 B2INFO(
"Comparing contents ...")
563 diff = difflib.SequenceMatcher(a=listA, b=listB)
564 table = [[
"",
"Name",
"Rev" if not args.checksums
else "Checksum"]]
565 columns = [1,
"+", -8
if not args.checksums
else -32]
567 if args.human_readable:
571 table[0] += [
"First Exp",
"First Run",
"Final Exp",
"Final Run"]
572 columns += [6, 6, 6, 6]
575 table[0] += [
"IovId",
"PayloadId"]
578 def add_payloads(opcode, payloads):
579 """Add a list of payloads to the table, filling the first column with opcode"""
581 row = [opcode, p.name, p.revision
if not args.checksums
else p.checksum]
582 if args.human_readable:
583 row += [p.readable_iov()]
588 row += [p.iov_id, p.payload_id]
591 for tag, i1, i2, j1, j2
in diff.get_opcodes():
595 add_payloads(
" ", listB[j1:j2])
596 if tag
in [
"delete",
"replace"]:
597 add_payloads(
"-", listA[i1:i2])
598 if tag
in [
"insert",
"replace"]:
599 add_payloads(
"+", listB[j1:j2])
601 if args.human_readable:
605 remove_repeated_values(table, [0, 1, 2] + ([-1]
if args.show_ids
else []), keep=[0])
607 def color_row(row, widths, line):
608 if not LogPythonInterface.terminal_supports_colors():
610 begin = {
'+':
'\x1b[32m',
'-':
'\x1b[31m'}.get(row[0],
"")
612 return begin + line + end
617 print(f
" Differences between {args.tagA} and {args.tagB}")
618 pretty_print_table(table, columns, transform=color_row,
619 hline_formatter=
lambda w:
" " + (w - 1) *
'-')
622 def command_iov(args, db):
624 List all IoVs defined in a globaltag, optionally limited to a run range
626 This command lists all IoVs defined in a given globaltag. The list can be
627 limited to a given run and optionally searched using --filter or --exclude.
628 If the --regex option is supplied the search term will be interpreted as a
629 python regular expression where the case is ignored.
631 .. versionchanged:: release-03-00-00
632 modified output structure and added ``--human-readable``
633 .. versionchanged:: after release-04-00-00
634 added parameter ``--checksums`` and ``--show-ids``
637 iovfilter = ItemFilter(args)
640 args.add_argument(
"tag", metavar=
"TAGNAME", help=
"globaltag for which the the IoVs should be listed")
641 args.add_argument(
"--run", type=int, nargs=2, metavar=
"N", help=
"exp and run numbers "
642 "to limit showing iovs to a ones present in a given run")
643 args.add_argument(
"--detail", action=
"store_true", default=
False,
644 help=
"if given show a detailed information for all "
645 "IoVs including details of the payloads")
646 args.add_argument(
"--human-readable", default=
False, action=
"store_true",
647 help=
"If given the iovs will be written in a more human friendly format. "
648 "Also repeated payload names will be omitted to create a more readable listing.")
649 args.add_argument(
"--checksums", default=
False, action=
"store_true",
650 help=
"If given don't show the revision number but the md5 checksum")
651 args.add_argument(
"--show-ids", default=
False, action=
"store_true",
652 help=
"If given also show the payload and iov ids for each iov")
653 iovfilter.add_arguments(
"payloads")
657 if not iovfilter.check_arguments():
660 if args.run
is not None:
661 msg =
"Obtaining list of iovs for globaltag {tag}, exp={exp}, run={run}{filter}".format(
662 tag=args.tag, exp=args.run[0], run=args.run[1], filter=iovfilter)
663 req = db.request(
"GET",
"/iovPayloads", msg, params={
'gtName': args.tag,
'expNumber': args.run[0],
664 'runNumber': args.run[1]})
666 msg = f
"Obtaining list of iovs for globaltag {args.tag}{iovfilter}"
667 req = db.request(
"GET",
"/globalTag/{}/globalTagPayloads".format(encode_name(args.tag)), msg)
669 with Pager(
"List of IoVs{}{}".format(iovfilter,
" (detailed)" if args.detail
else ""),
True):
671 for item
in req.json():
672 payload = item[
"payload" if 'payload' in item
else "payloadId"]
673 if "payloadIov" in item:
674 iovs = [item[
'payloadIov']]
676 iovs = item[
'payloadIovs']
678 if not iovfilter.check(payload[
'basf2Module'][
'name']):
685 iov_created = parse_date(iov[
"dtmIns"])
686 iov_modified = parse_date(iov[
"dtmMod"])
687 payload_created = parse_date(payload[
"dtmIns"])
688 payload_modified = parse_date(payload[
"dtmMod"])
690 [
"IoV Id", str(iov[
"payloadIovId"])],
691 [
"first experiment", iov[
"expStart"]],
692 [
"first run", iov[
"runStart"]],
693 [
"final experiment", iov[
"expEnd"]],
694 [
"final run", iov[
"runEnd"]],
695 [
"IoV created", iov_created.astimezone(tz=
None).strftime(
"%Y-%m-%d %H:%M:%S local time")],
696 [
"IoV modified", iov_modified.astimezone(tz=
None).strftime(
"%Y-%m-%d %H:%M:%S local time")],
697 [
"IoV modified by", iov[
"modifiedBy"]],
698 [
"payload Id", str(payload[
"payloadId"])],
699 [
"name", payload[
"basf2Module"][
"name"]],
700 [
"revision", payload[
"revision"]],
701 [
"checksum", payload[
"checksum"]],
702 [
"payloadUrl", payload[
"payloadUrl"]],
703 [
"baseUrl", payload.get(
"baseUrl",
"")],
705 [
"payload created", payload_created.astimezone(tz=
None).strftime(
"%Y-%m-%d %H:%M:%S local time")],
706 [
"payload modified", payload_modified.astimezone(tz=
None).strftime(
"%Y-%m-%d %H:%M:%S local time")],
707 [
"payload modified by", escape_ctrl_chars(payload[
"modifiedBy"])],
710 pretty_print_table(result, [-40,
'*'],
True)
712 payloads.append(PayloadInformation.from_json(payload, iov))
715 def add_ids(table, columns, payloads):
716 """Add the numerical ids to the table"""
718 table[0] += [
"IovId",
"PayloadId"]
720 for row, p
in zip(table[1:], payloads):
721 row += [p.iov_id, p.payload_id]
723 if args.human_readable:
724 table = [[
"Name",
"Rev" if not args.checksums
else "Checksum",
"IoV"]]
725 columns = [
"+", -8
if not args.checksums
else -32, -32]
726 table += [[p.name, p.revision
if not args.checksums
else p.checksum, p.readable_iov()]
for p
in payloads]
727 add_ids(table, columns, payloads)
729 remove_repeated_values(table, columns=[0, 1] + ([-1]
if args.show_ids
else []))
732 table = [[
"Name",
"Rev" if not args.checksums
else "Checksum",
"First Exp",
"First Run",
"Final Exp",
"Final Run"]]
733 table += [[p.name, p.revision
if not args.checksums
else p.checksum] + list(p.iov)
for p
in payloads]
734 columns = [
"+", -8
if not args.checksums
else -32, 6, 6, 6, 6]
735 add_ids(table, columns, payloads)
737 pretty_print_table(table, columns)
740 def command_dump(args, db):
742 Dump the content of a given payload
744 .. versionadded:: release-03-00-00
746 This command will dump the payload contents stored in a given payload. One
747 can either specify the payloadId (from a previous output of
748 ``b2conditionsdb iov``), the payload name and its revision in the central
749 database, or directly specify a local database payload file.
753 Dump the content of a previously downloaded payload file:
755 $ b2conditionsdb dump -f centraldb/dbstore_BeamParameters_rev_59449.root
757 Dump the content of a payload by name and revision directly from the central database:
759 $ b2conditionsdb dump -r BeamParameters 59449
761 Dump the content of the payload by name which is valid in a given globaltag
762 for a given experiment and run::
764 $ b2conditionsdb dump -g BeamParameters main_2021-08-04 0 0
766 Or directly by payload id from a previous call to ``b2conditionsdb iov``:
768 $ b2conditionsdb dump -i 59685
772 Depending on whether you want to display a payload by its id in the
773 database, its name and revision in the database or from a local file
774 provide **one** of the arguments ``-i``, ``-r``, ``-f`` or ``-g``
776 .. versionchanged:: after release-04-00-00
777 added argument ``-r`` to directly dump a payload valid for a given run
781 group = args.add_mutually_exclusive_group(required=
True)
782 choice = group.add_mutually_exclusive_group()
783 choice.add_argument(
"-i",
"--id", metavar=
"PAYLOADID", help=
"payload id to dump")
784 choice.add_argument(
"-r",
"--revision", metavar=(
"NAME",
"REVISION"), nargs=2,
785 help=
"Name and revision of the payload to dump")
786 choice.add_argument(
"-f",
"--file", metavar=
"FILENAME", help=
"Dump local payload file")
787 choice.add_argument(
"-g",
"--valid", metavar=(
"NAME",
"GLOBALTAG",
"EXP",
"RUN"), nargs=4,
788 help=
"Dump the payload valid for the given exp, run number in the given globaltag")
789 args.add_argument(
"--show-typenames", default=
False, action=
"store_true",
790 help=
"If given show the type names of all classes. "
791 "This makes output more crowded but can be helpful for complex objects.")
792 args.add_argument(
"--show-streamerinfo", default=
False, action=
"store_true",
793 help=
"If given show the StreamerInfo for the classes in the the payload file. "
794 "This can be helpful to find out which version of a payload object "
795 "is included and what are the members")
803 if not os.path.isfile(filename):
804 B2ERROR(f
"Payloadfile {filename} could not be found")
807 match = re.match(
r"^dbstore_(.*)_rev_(.*).root$", os.path.basename(filename))
809 match = re.match(
r"^(.*)_r(.*).root$", os.path.basename(filename))
811 B2ERROR(
"Filename doesn't follow database convention.\n"
812 "Should be 'dbstore_${payloadname}_rev_${revision}.root' or '${payloadname}_r${revision.root}'")
814 name = match.group(1)
815 revision = match.group(2)
816 payloadId =
"Unknown"
821 req = db.request(
"GET", f
"/payload/{args.id}",
"Getting payload info")
822 payload = PayloadInformation.from_json(req.json())
825 name, rev = args.revision
827 req = db.request(
"GET", f
"/module/{encode_name(name)}/payloads",
"Getting payload info")
829 if p[
"revision"] == rev:
830 payload = PayloadInformation.from_json(p)
833 B2ERROR(f
"Cannot find payload {name} with revision {rev}")
836 name, globaltag, exp, run = args.valid
838 for p
in db.get_all_iovs(globaltag, exp, run, f
", name={name}"):
839 if p.name == name
and (payload
is None or p.revision > payload.revision):
843 B2ERROR(f
"Cannot find payload {name} in globaltag {globaltag} for exp,run {exp},{run}")
846 filename = payload.url
847 revision = payload.revision
848 payloadId = payload.payload_id
852 from ROOT
import TFile, TBufferJSON, cout
855 tfile = TFile.Open(filename)
858 if not tfile
or not tfile.IsOpen():
860 contents = db._session.get(filename, stream=
True)
861 if contents.status_code != requests.codes.ok:
862 B2ERROR(f
"Could not open payload file {filename}")
864 raw_contents = contents.raw.read().decode()
866 obj = tfile.Get(name)
868 json_str = TBufferJSON.ConvertToJSON(obj)
872 Drop some members from ROOT json output.
874 We do not care about fBits, fUniqueID or the typename of sub classes,
875 we assume users are only interested in the data stored in the member
878 obj.pop(
"fBits",
None)
879 obj.pop(
"fUniqueID",
None)
880 if not args.show_typenames:
881 obj.pop(
"_typename",
None)
884 with Pager(f
"Contents of Payload {name}, revision {revision} (id {payloadId})",
True):
885 if args.show_streamerinfo
and tfile:
886 B2INFO(
"StreamerInfo of Payload {name}, revision {revision} (id {payloadId})")
887 tfile.ShowStreamerInfo()
894 if json_str
is not None:
895 B2INFO(f
"Contents of Payload {name}, revision {revision} (id {payloadId})")
898 obj = json.loads(json_str.Data(), object_hook=drop_fbits)
900 pprint.pprint(obj, compact=
True, width=shutil.get_terminal_size((80, 20))[0])
902 B2INFO(f
"Raw contents of Payload {name}, revision {revision} (id {payloadId})")
903 print(escape_ctrl_chars(raw_contents))
905 B2INFO(f
"ROOT contents of Payload {name}, revision {revision} (id {payloadId})")
906 B2WARNING(
"The payload is a valid ROOT file but doesn't contain a payload object with the expected name. "
907 " Automated display of file contents are not possible, showing just entries in the ROOT file.")
912 """Class to recursively show help for an ArgumentParser and all it's sub_parsers"""
915 """Print help message for given parser and call again for all sub parsers"""
917 subparsers_actions = [
918 action
for action
in parser._actions
919 if isinstance(action, argparse._SubParsersAction)]
922 for subparsers_action
in subparsers_actions:
924 for choice, subparser
in subparsers_action.choices.items():
926 print(f
"Command '{prefix}{choice}'")
927 print(subparser.format_help())
929 self.
print_subparsersprint_subparsers(subparser, prefix=f
"{prefix}{choice} ")
931 def __call__(self, parser, namespace, values, option_string=None):
932 """Show full help message"""
934 with Pager(f
"{parser.prog} {option_string}"):
940 def get_argument_parser():
942 Build a parser with all arguments of all commands
945 options = argparse.ArgumentParser(add_help=
False)
946 options.add_argument(
"--debugging", action=
"store_true",
947 help=
"Enable debugging of http traffic")
948 options.add_argument(
"--help-full", action=FullHelpAction,
949 help=
"show help message for all commands and exit")
950 options.add_argument(
"--base-url", default=
None,
951 help=
"URI for the base of the REST API, if not given a list of default locations is tried")
952 options.add_argument(
"--http-auth", choices=[
"none",
"basic",
"digest"], default=
"basic",
953 help=argparse.SUPPRESS)
954 options.add_argument(
"--http-user", default=
"commonDBUser", help=argparse.SUPPRESS)
955 options.add_argument(
"--http-password", default=
"Eil9ohphoo2quot", help=argparse.SUPPRESS)
957 parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, parents=[options])
958 parser.set_defaults(func=
lambda x, y: parser.print_help())
959 parsers = parser.add_subparsers(
960 title=
"Top level commands",
961 description=
"To get additional help, run '%(prog)s COMMAND --help'"
966 for name, func
in sorted(globals().items()):
967 if not name.startswith(
"command_"):
972 parts = name.split(
'_')[1:]
978 parent_parser, parent = subparsers[tuple(parts[:-1])]
982 parent = parent_parser.add_subparsers(
983 title=
"sub commands",
984 description=
"To get additional help, run '%(prog)s COMMAND --help'"
986 subparsers[tuple(parts[:-1])][1] = parent
991 helptxt, description = textwrap.dedent(func.__doc__).split(
"\n\n", 1)
992 command_parser = parent.add_parser(parts[-1], help=helptxt, add_help=
True, description=description,
993 parents=[options], formatter_class=argparse.RawDescriptionHelpFormatter)
996 func(command_parser,
None)
998 command_parser.set_defaults(func=func)
1000 subparsers[tuple(parts)] = [command_parser,
None]
1005 def create_symlinks(base):
1006 """Create symlinks from base to all subcommands.
1008 e.g. if the base is ``b2conditionsdb`` then this command will create symlinks
1009 like ``b2conditionsdb-tag-show`` in the same directory
1011 When adding a new command to b2conditionsdb this function needs to be executed
1012 in the framework tools directory
1014 python3 -c 'from conditions_db import cli_main; cli_main.create_symlinks("b2conditionsdb")'
1020 for name
in sorted(globals().keys()):
1021 if not name.startswith(
"command_"):
1023 parts = name.split(
"_")[1:]
1024 if parts
in excluded:
1026 dest = base +
"-".join([
""] + parts)
1030 except FileNotFoundError:
1032 print(f
"create symlink {dest}")
1033 os.symlink(base, dest)
1038 Main function for the command line interface.
1040 it will automatically create an ArgumentParser including all functions which
1041 start with command_ in the global namespace as sub commmands. These
1042 functions should take the arguments as first argument and an instance of the
1043 ConditionsDB interface as second argument. If the db interface is None the
1044 first argument is an instance of argparse.ArgumentParser an in this case the
1045 function should just add all needed arguments to the argument parser and
1050 logging.enable_summary(
False)
1052 logging.enable_python_logging =
True
1054 for level
in LogLevel.values.values():
1055 logging.set_info(level, LogInfo.LEVEL | LogInfo.MESSAGE)
1059 sys.argv[0:1] = os.path.basename(sys.argv[0]).split(
'-')
1062 parser = get_argument_parser()
1066 args = parser.parse_args()
1073 nprocess = getattr(args,
"nprocess", 1)
1074 retries = getattr(args,
"retries", 0)
1077 B2WARNING(
"-j must be larger than zero, ignoring")
1078 args.nprocess = nprocess = 1
1080 conditions_db =
ConditionsDB(args.base_url, nprocess, retries)
1082 if args.http_auth !=
"none":
1083 conditions_db.set_authentication(args.http_user, args.http_password, args.http_auth ==
"basic")
1086 return args.func(args, conditions_db)
def print_subparsers(self, parser, prefix="")
def __call__(self, parser, namespace, values, option_string=None)
int main(int argc, char **argv)
Run all tests.