3 from PyCool
import cool
6 import CoolConvUtilities.AtlCoolLib
as AtlCoolLib
10 from posixpath
import normpath
14 Expands a connect string.
16 This expansion can occur when a connect string without a containing
17 '://' or not in the format 'alias/DBNAME' is specified. In this case
18 the string is interpreted as a sqlite file name and rewritten to a
19 COOL compliant format:
21 TEST.db --> 'sqlite://;schema=TEST.db;dbname=TEST'
22 TEST --> 'sqlite://;schema=TEST;dbname=TEST'
24 The filename can have a '.db' suffix which will be stripped for the
25 'dbname' part of the connect string. Other suffixes will not be recognized.
27 Actually for ATLAS, a simple string without / is interpreted as a database
28 instance name within the SQLite file mycool.db, for consistency with
31 if connectString.find(
'://' ) == -1:
32 if connectString.endswith(
'.db' ):
33 base = connectString[:-3]
34 elif match(
"[a-zA-Z0-9_-]+/[A-Z0-9_-]{1,8}",connectString):
40 return (
'sqlite://;schema=mycool.db;dbname=%s' % base)
45 def connect( connectString, verbose = False):
47 Connects to the given database and returns a tuple
48 database, connectString
49 where 'database' is a cool.IDatabase object and 'connectString' is the
50 possibly expanded connectString that 'database' is based on.
52 This expansion can occur when a connect string without a containing
53 '://' is specified. In this case the string is interpreted as a sqlite
54 file name and rewritten to a RAL compliant format:
56 TEST.db --> 'sqlite://;schema=TEST.db;dbname=TEST'
57 TEST --> 'sqlite://;schema=TEST;dbname=TEST'
59 The filename can have a '.db' suffix which will be stripped for the
60 'dbname' part of the connect string. Other suffixes will not be recognized.
62 Note that the COOL database inside the file must have the same name as the
63 base of the filename for this shortcut to work. Storing a COOL database
64 MYTEST in a file mytest.db will not work.
66 Set verbose to True to obtain an error print out.
70 if (
';readoracle' in connectString):
73 connectString=connectString.replace(
';readoracle',
'')
75 if (
';readsqlite' in connectString):
76 connectString=connectString.replace(
';readsqlite',
'')
79 dbSvc = cool.DatabaseSvcFactory.databaseService()
82 if (
'oracle' in connectString
or 'mysql' in connectString
or 'sqlite' in connectString): readonly=
False
83 db=AtlCoolLib.indirectOpen(connectString,readonly,debug)
84 except Exception
as e:
85 if 'The database does not exist' in str(e):
86 print (
"Creating new database")
87 db = dbSvc.createDatabase( connectString )
89 if verbose:
print (
'Error while connecting:',
str(e))
91 return db, connectString
94 typeSizes = { cool.StorageType.Bool : 1,
95 cool.StorageType.UChar : 1,
96 cool.StorageType.Int16 : 2,
97 cool.StorageType.UInt16 : 2,
98 cool.StorageType.Int32 : 4,
99 cool.StorageType.UInt32 : 4,
100 cool.StorageType.UInt63 : 8,
101 cool.StorageType.Int64 : 8,
102 cool.StorageType.Float : 4,
103 cool.StorageType.Double : 8,
104 cool.StorageType.String255 : 255,
105 cool.StorageType.String4k : 4000,
106 cool.StorageType.String64k : 65535,
107 cool.StorageType.String16M : 16777215,
108 cool.StorageType.Blob64k : 65535,
109 cool.StorageType.Blob16M : 16777215,
114 Determines the (effective) byte size of an AttributeListSpecification.
115 This does not account for any extra size for the specification's own
116 structure but just the byte sizes of the attributes. In other words an
117 AttributeListSpecification with a single 'int' fields will report a byte
122 size += typeSizes[i.storageType().
id()]
129 This class extends a dictionary with a format string for its representation.
130 It is used as the return value for the inspection commands, such that one
131 can both query for the inspected object's information:
133 res = tool.ls( '/a' )
136 as well as obtain a standard printout of the information:
147 return super( Info, self ).
__str__()
154 This class extends a list with a custom string representation.
157 return '\n'.
join( [
str(i)
for i
in self ] )
167 Initialize the object to a given database.
169 If 'database' is of type string, it must be a RAL compliant connect
170 string or an sqlite filename following the requirements described
173 Otherwise it must be a valid cool.IDatabase object.
175 if isinstance(database, str):
176 self.
db, connectString =
connect( database )
183 self.
curtimes=[cool.ValidityKeyMin,cool.ValidityKeyMax]
188 return self.
db.databaseId()
190 def cd(self,node='/'):
191 if node.startswith(
"/"):
194 npath=normpath(self.
curdir+
"/"+node)
196 if npath.startswith(
"//"): npath=npath[1:]
198 if self.
db.existsFolder(npath)
or self.
db.existsFolderSet(npath):
201 raise Exception(
'Node %s does not exist' % node)
206 res.append(
'Current directory: %s' % self.
curdir)
214 if self.
db.existsFolder(self.
curdir):
224 if (itag<len(staglist)):
225 self.
curtag=staglist[itag]
233 res.append(
'Changed current tag selection to %s' % self.
curtag)
237 if (chan==
"ALL" or chan==
"all"):
244 res.append(
'Changed current channel selection to %i' % self.
curchan)
246 res.append(
'Changed current channel selection to ALL')
251 res.append(
'Current tag selection: %s' % self.
curtag)
253 res.append(
'Current channel selection: ALL')
255 res.append(
'Current channel selection: %i' % self.
curchan)
259 if node
is None or node ==
'' or node ==
'.':
261 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
262 if (node.startswith(
'//')): node=node[1:]
264 chk=
input(
'Delete folder(set) '+node+
' ? (y/n)')
265 if not (chk==
'y' or chk==
'Y'):
266 res.append(
'Deletion aborted!')
268 if self.
db.existsFolder(node)
or self.
db.existsFolderSet(node):
270 retcode=self.
db.dropNode(node)
271 res.append(
'Folder dropped with return code '+
str(retcode))
272 except Exception
as e:
274 res.append(
'Could not drop folder')
276 res.append(
'Folder '+node+
' does not exist')
279 def less( self, node, header = False, more=False ):
280 if node
is None or node ==
'' or node ==
'.':
282 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
283 if (node.startswith(
'//')): node=node[1:]
285 if self.
db.existsFolder( node ):
286 f = self.
db.getFolder( node )
287 fdesc=f.description()
289 chansel=cool.ChannelSelection.all()
291 chansel=cool.ChannelSelection(self.
curchan)
292 res.append(
'Using channel: %i' % self.
curchan)
293 istime=(fdesc.find(
'<timeStamp>time')!=-1)
300 if (limmin!=cool.ValidityKeyMin
or limmax!=cool.ValidityKeyMax):
301 res.append(
'Using rawIOV range [%i,%i]' % (limmin,limmax))
305 if self.
curtag not in f.listTags():
308 restag=f.resolveTag(self.
curtag)
309 res.append(
'Using tag selection: %s resolved to %s' % (self.
curtag,restag))
311 res.append(
'Tag %s not defined here' % self.
curtag)
315 res.append(
'Using tag selection: %s' % self.
curtag)
316 coolvec=(fdesc.find(
'CondAttrListVec')>=0
and fdesc.find(
'coracool')<0)
317 if coolvec:
print (
"Folder has CoolVector payload")
318 objs = f.browseObjects( limmin,limmax,chansel,restag )
319 while objs.goToNext():
321 obj=objs.currentRef()
322 i=self.
timeRep(obj.since(),istime)+
" - "+self.
timeRep(obj.until(),istime,
True)+
" ("+
str(obj.channelId())+
")"
325 pitr=obj.payloadIterator()
326 while pitr.goToNext():
327 pobj=pitr.currentRef()
328 i+=
'\nVector element %i:' % j
334 i =
Info(
' %(str)s' )
335 i[
'str'] =
str(objs.currentRef())
338 raise Exception(
"Node '%s' is not a folder" % node )
342 def more( self, node, header=False ):
343 return self.
less(node,header,
True)
346 "Print COOL Validity Key in run/LB or time notation"
350 if (value==cool.ValidityKeyMin):
351 return "ValidityKeyMin"
352 elif (cool.ValidityKeyMax-value<1000000000):
353 return "ValidityKeyMax"
355 stime=
int(value/1000000000)
356 return time.asctime(time.gmtime(stime))+
" UTC"
358 return "[%i,%i%s" % (value >> 32, value & 0xFFFFFFFF,trail)
361 "Pretty-print the payload of an object - helper function for more cmd"
362 spec=payload.specification()
364 for idx
in range(spec.size()):
366 typename=spec[idx].storageType().
name()
367 i+=
" ["+spec[idx].
name() +
" (" + typename +
") : "
368 if (typename.startswith(
"Blob")):
371 if isinstance(blobdata,bytes):
372 chksum=crc32(blobdata)
374 chksum=crc32(blobdata.encode())
375 i+=
"size=%i,chk=%d" % (blob.size(),chksum)
377 i+=
str(payload[idx])
382 res =
Info(
' %(name)-16s %(description)-16s'
383 ' %(cardinality)-8s %(size)-12s' )
385 res[
'description'] =
'Description'
386 res[
'cardinality'] =
'Count'
392 f = self.
db.getFolder( node )
395 res[
'description'] = f.description()
397 res[
'cardinality'] = f.countObjects( cool.ValidityKeyMin,
399 cool.ChannelSelection.all() )
400 res[
'size'] = res[
'cardinality'] *
byteSize( f.payloadSpecification() )
402 res[
'cardinality']=
'-'
408 res =
Info(
' %(name)-16s %(description)-16s' )
410 res[
'description'] =
'Description'
415 f = self.
db.getFolderSet( node )
418 res[
'description'] = f.description()
422 def ls( self, node = '/', header = False, doCount=False ):
423 if node
is None or node ==
'' or node ==
'.':
425 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
426 if (node.startswith(
'//')): node=node[1:]
429 if self.
db.existsFolderSet( node ):
430 fs = self.
db.getFolderSet( node )
432 if header
and fs.listFolderSets():
434 for n
in fs.listFolderSets():
437 if header
and fs.listFolders():
439 for n
in fs.listFolders():
442 raise Exception(
"Node '%s' is not a folderset" % node )
446 if node
is None or node ==
'' or node ==
'.':
448 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
449 if (node.startswith(
'//')): node=node[1:]
452 if self.
db.existsFolder(node):
453 f=self.
db.getFolder(node)
454 res.append(
'Listing tags for folder '+node)
455 elif self.
db.existsFolderSet(node):
456 f=self.
db.getFolderSet(node)
457 res.append(
'Listing tags for folderset '+node)
460 if pattern==
"showObsolete":
464 res.append(
'Only listing tags containing: "%s"' % pattern)
475 if tag.find(pattern)<0:
continue
478 if (
not showObsolete)
and (f.tagDescription(tag).
find(
'OBSOLETE')>-1):
continue
480 locked=f.tagLockStatus(tag)
481 if (locked==cool.HvsTagLock.UNLOCKED):
483 elif (locked==cool.HvsTagLock.PARTIALLYLOCKED):
484 strlock=
'partially locked'
487 desc=f.tagDescription(tag)
488 res.append(
'%s (%s) [%s]' % (tag,strlock,desc))
490 raise Exception(
"Node '%s' does not exist" % node)
494 if node
is None or node ==
'' or node ==
'.':
496 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
497 if (node.startswith(
'//')): node=node[1:]
500 if self.
db.existsFolder(node):
501 f=self.
db.getFolder(node)
502 res.append(
'Listing channel IDs, names, descriptions for folder'+node)
503 chanlist=f.listChannels()
504 res.append(
'Total number of channels defined: %i' % len(chanlist))
505 res.append(
'Seq ChannelNum ChannelName Desc')
507 for ichan
in chanlist:
509 channame=f.channelName(ichan)
513 chandesc=f.channelDescription(ichan)
516 res.append(
'%i: %i %s %s' % (iseq,ichan,channame,chandesc))
519 raise Exception(
"Node '%s' is not a folder" % node)
523 if node
is None or node ==
'' or node ==
'.':
525 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
526 if (node.startswith(
'//')): node=node[1:]
528 if self.
db.existsFolder(node):
529 f=self.
db.getFolder(node)
530 if (f.versioningMode()==cool.FolderVersioning.MULTI_VERSION):
534 res.append(
'Specification for %s-version folder %s' % (vstr,node))
535 fspec=f.payloadSpecification()
537 res.append(
' %s (%s)' % ( field.name(),field.storageType().
name()))
538 res.append(
'Description: %s' % f.description())
540 res.append(
'Node %s is not a folder' % node)
544 args=argumentString.split()
546 raise Exception(
'Usage: settag <folder> <foldertag> <parenttag>')
550 if node
is None or node ==
'' or node ==
'.':
552 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
553 if (node.startswith(
'//')): node=node[1:]
556 if self.
db.existsFolder(node):
557 f=self.
db.getFolder(node)
558 res.append(
'Setting tag '+tag1+
' for folder '+node+
' to parent tag '+tag2)
559 elif self.
db.existsFolderSet(node):
560 f=self.
db.getFolderSet(node)
561 res.append(
'Setting tag '+tag1+
' for folderset '+node+
' to parent tag '+tag2)
564 if tag1
not in f.listTags():
565 print (
"WARNING: Tag %s does not exist in node %s" % (tag1,node))
566 chk=
input(
"Do you want to proceed anyway (y/n)")
567 if (chk.upper()!=
"Y"):
568 raise Exception(
'ABORTED - Tag %s does not exist' % tag1)
570 f.createTagRelation(tag2,tag1)
571 except Exception
as e:
573 res.append(
'createTagRelation fails')
575 raise Exception(
"Node '%s' does not exist" % node)
579 args=argumentString.split()
581 raise Exception(
'Usage: setchan <folder> <chanID> <chanName> {<chanDescr>}')
589 if node
is None or node ==
'' or node ==
'.':
591 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
592 if (node.startswith(
'//')): node=node[1:]
595 if self.
db.existsFolder(node):
596 f=self.
db.getFolder(node)
597 if (f.existsChannel(chanid)):
598 res.append(
'Resetting data for existing channel %i %s %s' % (chanid,channame,chandesc))
599 f.setChannelName(chanid,channame)
601 f.setChannelDescription(chanid,chandesc)
604 res.append(
'Creating data for channel %i %s %s' % (chanid ,channame,chandesc))
605 f.createChannel(chanid,channame,chandesc)
608 res.append(
'Node %s is not a folder' % node)
612 args=argumentString.split()
614 raise Exception(
'Usage: settginfo <folder> <tag> <description>')
617 desc=
' '.
join(args[2:])
618 if node
is None or node ==
'' or node ==
'.':
620 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
621 if (node.startswith(
'//')): node=node[1:]
624 if self.
db.existsFolder(node):
625 f=self.
db.getFolder(node)
626 elif self.
db.existsFolderSet(node):
627 f=self.
db.getFolderSet(node)
629 res.append(
'Node %s does not exist' % node)
631 res.append(
'Setting description for tag %s to %s' % (tag,desc))
632 f.setTagDescription(tag,desc)
636 args=argumentString.split()
643 raise Exception(
'Usage: userunlumi <run1> {<LB1> <run2> <LB2>}')
653 args=argumentString.split()
655 raise Exception(
'Usage: usetimes <t1> <t2>')
661 ts=time.strptime(args[i]+
'/UTC',
'%Y-%m-%d:%H:%M:%S/%Z')
662 self.
curtimes[i]=
int(calendar.timegm(ts))*1000000000
664 print (
"ERROR in time specification, use e.g. 2007-05-25:14:01:00")
668 args=argumentString.split()
670 raise Exception(
'Usage: rmtag <folder> <parenttag>')
673 if node
is None or node ==
'' or node ==
'.':
675 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
676 if (node.startswith(
'//')): node=node[1:]
679 if self.
db.existsFolder(node):
680 f=self.
db.getFolder(node)
681 res.append(
'Removing association of tag '+tag1+
' to folder '+node)
682 elif self.
db.existsFolderSet(node):
683 f=self.
db.getFolderSet(node)
684 res.append(
'Removing association of tag '+tag1+
' to folderset '+node)
688 if (tag1
in taglist):
692 res.append(
'Removal of leaf tag '+tag1+
' succeeded')
693 except Exception
as e:
695 res.append(
'deleteTag fails')
699 f.deleteTagRelation(tag1)
700 except Exception
as e:
702 res.append(
'deleteTagRelation fails')
704 raise Exception(
"Node '%s' does not exist" % node)
708 args=argumentString.split()
710 raise Exception(
'Usage: headtag <folder> <tag>')
713 if node
is None or node ==
'' or node ==
'.':
715 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
716 if (node.startswith(
'//')): node=node[1:]
719 if self.
db.existsFolder(node):
720 f=self.
db.getFolder(node)
721 res.append(
'Applying tag '+tag+
' to HEAD of folder '+node)
723 f.tagCurrentHead(tag,
"AtlCoolConsole tag")
724 except Exception
as e:
726 res.append(
'tagCurrentHead fails')
728 raise Exception(
"Node '%s' does not exist" % node)
732 args=argumentString.split()
734 raise Exception(
'Usage: locktag <folder> <tag> {action=l|p|u|r}')
739 action=args[2].
upper()
740 if node
is None or node ==
'' or node ==
'.':
742 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
743 if (node.startswith(
'//')): node=node[1:]
746 if self.
db.existsFolder(node):
747 f=self.
db.getFolder(node)
750 res.append(
'Folder %s is a leaf node, ignoring recursion' % node)
751 elif self.
db.existsFolderSet(node):
752 f=self.
db.getFolderSet(node)
754 raise Exception(
"Node '%s' does not exist" % node)
755 if tag
not in f.listTags():
756 raise Exception(
'Tag %s does not exist in node %s' % (tag,node))
757 state=cool.HvsTagLock.LOCKED
758 if (action==
'U'): state=cool.HvsTagLock.UNLOCKED
759 if (action==
'P'): state=cool.HvsTagLock.PARTIALLYLOCKED
763 nodelist=self.
db.listAllNodes()
764 for inode
in nodelist:
765 if (inode[:len(node)]==node):
768 if (self.
db.existsFolder(inode)):
769 subf=self.
db.getFolder(inode)
770 if (subf.versioningMode==cool.FolderVersioning.SINGLE_VERSION): multi=
False
772 subf=self.
db.getFolderSet(inode)
775 rtag=subf.resolveTag(tag)
777 if (rtag.find(
'UPD1')==-1
and rtag.find(
'UPD2')==-1):
778 curstate=subf.tagLockStatus(rtag)
779 if (curstate!=state):
780 subf.setTagLockStatus(rtag,state)
782 res.append(
'Set lock for tag %s to %i' % (rtag,subf.tagLockStatus(rtag)))
784 res.append(
'Tag %s already at state %i' % (rtag,curstate))
786 res.append(
'Skip tag %s due to UPD mode' % rtag)
789 res.append(
'Changed state of %i tags' % nmod)
792 f.setTagLockStatus(tag,state)
793 res.append(
'Set lock for tag %s to %i' % (tag,f.tagLockStatus(tag)))
798 args=argumentString.split()
800 raise Exception(
'Usage: tracetags <folder> <foldertag>')
803 if node
is None or node ==
'' or node ==
'.':
805 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
806 if (node.startswith(
'//')): node=node[1:]
809 if self.
db.existsFolderSet(node):
810 f=self.
db.getFolderSet(node)
812 raise Exception(
"Node '%s' does not exist" % node)
815 if tag
not in taglist:
816 raise Exception(
"Tag '%s' does not exist in node '%s'" % (tag,node))
818 res.append(
'Searching under %s for tags referenced by %s' % (node,tag))
819 nodelist=self.
db.listAllNodes()
820 for inode
in nodelist:
822 if (inode[:len(node)]==node):
825 if (self.
db.existsFolder(inode)):
826 subf=self.
db.getFolder(inode)
827 if (subf.versioningMode==cool.FolderVersioning.SINGLE_VERSION): multi=
False
829 subf=self.
db.getFolderSet(inode)
832 rtag=subf.resolveTag(tag)
835 res.append(
'Folder %s : tag %s' % (inode,rtag))
839 args=argumentString.split()
841 raise Exception(
'Usage: clonetag <folder> <sourcetag> <desttag>')
845 if node
is None or node ==
'' or node ==
'.':
847 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
848 if (node.startswith(
'//')): node=node[1:]
850 if self.
db.existsFolder(node):
851 f=self.
db.getFolder(node)
853 if (tag2.find(
'UPD')>=0
and tag2
in f.listTags()):
854 raise Exception(
'Cannot clone to existing UPDx destination tag %s' % tag2)
855 res.append(
'Cloning tag '+tag1+
' for folder '+node+
' to dest tag '+tag2)
857 f.cloneTagAsUserTag(tag1,tag2)
858 res.append(
'All done')
859 except Exception
as e:
860 res.append(
'cloneTagAsUserTag failed with error %s' % e)
861 elif self.
db.existsFolderSet(node) :
863 res.append(
'Cloning tag '+tag1+
' for folder '+node+
' to dest tag '+tag2)
864 nodelist=self.
db.listAllNodes()
865 for inode
in nodelist:
866 if (inode[:len(node)+1]==node+
'/')
or (node==
'/' and inode[:len(node)]==node):
867 if self.
db.existsFolder(inode):
868 finode=inode[:inode.rfind(
'/')]
869 if finode==(
'') : finode=
'/'
872 finode=inode[:inode.rfind(
'/')]
873 if finode==(
'') : finode=
'/'
875 subf=self.
db.getFolder(inode)
877 subf=self.
db.getFolderSet(inode)
879 rtag=subf.resolveTag(tag1)
881 if rtag
not in addedtag :
882 addedtag.append(rtag)
883 subf.createTagRelation(tag2,rtag)
884 except Exception
as e:
886 res.append(
'createTagRelation failed with error %s' %e)
888 res.append(
'Folder %s : no tag selected' % inode)
889 res.append(
'All done')
891 raise Exception(
"Node '%s' does not exist" % node)
896 args=argumentString.split()
898 raise Exception(
'Usage: setdesc <folder> {<descrption>}')
904 if node
is None or node ==
'' or node ==
'.':
906 elif not node.startswith(
'/'): node=self.
curdir+
'/'+node
907 if (node.startswith(
'//')): node=node[1:]
909 if desc==
"ATTRTIME" :
911 desc=
'<timeStamp>time</timeStamp><addrHeader><address_header service_type="71" clid="40774348" /></addrHeader><typeName>AthenaAttributeList</typeName>'
912 elif desc==
"ATTRRUN" :
914 desc=
'<timeStamp>run-event</timeStamp><addrHeader><address_header service_type="71" clid="40774348" /></addrHeader><typeName>AthenaAttributeList</typeName>'
915 elif desc==
"ATTRCOLLTIME" :
917 desc=
'<timeStamp>time</timeStamp><addrHeader><address_header service_type="71" clid="1238547719" /></addrHeader><typeName>CondAttrListCollection</typeName>'
918 elif desc==
"ATTRCOLLRUN" :
920 desc=
'<timeStamp>run-event</timeStamp><addrHeader><address_header service_type="71" clid="1238547719" /></addrHeader><typeName>CondAttrListCollection</typeName>'
924 if self.
db.existsFolder(node):
925 f=self.
db.getFolder(node)
926 res.append(
'Current folder description: %s' % f.description())
928 res.append(
'Set folder description to: %s' % desc)
930 f.setDescription(desc)
931 except Exception
as e:
933 res.append(
'Set folder description failed')
935 raise Exception(
"Node '%s' does not exist" % node)
939 if self.
db is not None:
940 return "Connected to '%s'" % self.
db.databaseId()
942 return "Not connected"