8 Module defining utilities for ATLAS command line/python use of COOL
11 import sys,os,getopt,time,calendar
12 from PyCool
import cool
19 Translate simple connection string (no slash) to mycool.db with given
20 instance, all others left alone
23 return 'sqlite://X;schema=mycool.db;dbname='+conn
29 Open COOL database with given connection, or force open if not possible
30 Return COOL database object, or None if cannot be opened
31 Database is opened in update mode
33 dbSvc=cool.DatabaseSvcFactory.databaseService()
36 db=dbSvc.openDatabase(conn2,
False)
37 except Exception
as e:
39 print (
'Could not connect to',conn,
'try to create it')
41 db=dbSvc.createDatabase(conn2)
42 except Exception
as e:
44 print (
'Could not create the database - give up')
50 Open COOL database with given connection for reading
52 dbSvc=cool.DatabaseSvcFactory.databaseService()
55 db=dbSvc.openDatabase(conn2,
True)
56 except Exception
as e:
58 print (
'Could not connect to',conn)
62 def ensureFolder(db,folder,spec,desc,version=cool.FolderVersioning.SINGLE_VERSION):
64 Ensure folder exists, creating it if needed with given spec
65 Return COOL folder object
67 if (
not db.existsFolder(folder)):
68 print (
'Attempting to create',folder,
'with description',desc)
71 folderSpec = cool.FolderSpecification(version,spec)
72 db.createFolder(folder,folderSpec,desc,
True)
73 print (
'Folder created')
74 except Exception
as e:
76 print (
'Could not create folder',folder)
78 return db.getFolder(folder)
82 Return the correct folder description string for Athena IOVDbSVc access
83 runLumi=True or False for timestamp
84 datatype= { AthenaAttributeList | CondAttrListCollection }
87 desc=
'<timeStamp>run-lumi</timeStamp>'
89 desc=
'<timeStamp>time</timeStamp>'
93 if (datatype==
'CondAttrListCollection'):
95 tname=
'CondAttrListCollection'
96 elif (datatype==
'AthenaAttributeList'):
98 tname=
'AthenaAttributeList'
99 elif (datatype==
'CondAttrListVec'):
101 tname=
'CondAttrListVec'
103 desc+=
'<addrHeader><address_header service_type=\"'+
str(stype)+
'\" clid=\"'+
str(clid)+
'\" /></addrHeader><typeName>'+tname+
'</typeName>'
107 "Convert argument to time in seconds, treating as a literal or date"
113 ts=time.strptime(val+
'/UTC',
'%Y-%m-%d:%H:%M:%S/%Z')
114 return int(calendar.timegm(ts))
116 print (
"ERROR in time specification, use e.g. 2007-05-25:14:01:00")
120 "Convert the IOVkey (63 bit) to a string representing timestamp"
121 if (iovkey==cool.ValidityKeyMin):
122 return "ValidityKeyMin"
123 elif (iovkey==cool.ValidityKeyMax):
124 return "ValidityKeyMax"
126 stime=
int(iovkey/1000000000)
127 return time.asctime(time.gmtime(stime))+
" UTC"
130 """Obtain a connection to the database coolstr (e.g.
131 COOLONL_INDET/OFLP200) using Oracle servers only, bypassing SQLite files
132 and simulating the action of DBReplicaSvc to choose the correct one.
133 Returns None in case a connection cannot be established.
134 debug=True produces debug printout to show servers being tried"""
135 dbSvc=cool.DatabaseSvcFactory.databaseService()
139 splitname=connstr.split(
'/')
140 forceSQLite=
'ATLAS_COOL_FORCESQLITE' in os.environ
141 if (debug
and forceSQLite):
142 print (
"ATLAS_COOL_FORCESQLITE: Force consideration of SQLite replicas")
143 if (len(splitname)!=2
or readOnly
is False or forceSQLite):
145 db=dbSvc.openDatabase(connstr,readOnly)
146 except Exception
as e:
149 if (
not readOnly
and (
'attempt to write a readonly database' in e.__repr__())):
150 print (
"Writeable open failed - retry in readonly mode")
151 db=dbSvc.openDatabase(connstr,
True)
155 if (debug):
print (
"Trying to establish connection for %s from Oracle" % connstr)
160 if (debug):
print (serverlist)
163 while len(serverlist)>0:
164 server=serverlist.pop()
167 if (
'FRONTIER_SERVER' in os.environ):
168 connstr=
'frontier://ATLF/();schema=ATLAS_%s;dbname=%s' % (schema,dbinst)
172 elif server==
'atlas_dd':
continue
174 connstr=
'oracle://%s;schema=ATLAS_%s;dbname=%s' % (server,schema,dbinst)
175 if (debug):
print (
"Attempting to open %s" % connstr)
177 dbconn=dbSvc.openDatabase(connstr)
178 if (dbconn
is not None and dbconn.isOpen()):
179 if (debug):
print (
"Established connection to %s" % server)
181 if (debug):
print (
"Cannot connect to %s" % server)
183 if (debug):
print (
"Exception connecting to %s" % server)
185 print (
"All available replicas tried - giving up")
189 """Return list of Oracle database server names to try, mimicing action of
190 Athena DBReplicaSvc"""
192 if configfile
is None:
193 print (
"Cannot find dbreplica.config")
198 for line
in configfile.readlines():
200 if (epos>0
and line[0]!=
"#"):
201 domains=line[0:epos].
split()
203 if ((hostname[-len(dom):]==dom
and len(dom)>best)
or (best==0
and dom==
'default')):
205 serverlist=line[epos+1:].
split()
207 if (len(serverlist)==0):
208 print (
"No suitable servers found")
213 """Find the file leaf using the DATAPATH variable to look for it
214 Return a read-only file object, or None if not found.
215 If retFile is false, just return the pathname.
216 Current directory takes precendence if file is there"""
218 paths=os.environ[
'DATAPATH'].
split(
':')
225 os.stat(path+
'/'+leaf)
227 return open(path+
'/'+leaf,
'r')
235 "Get the hostname including domain name if possible"
236 if (
'ATLAS_CONDDB' in os.environ):
237 name=os.environ[
'ATLAS_CONDDB']
240 name=os.environ[
'HOSTNAME']
245 name=socket.getfqdn()
250 if (name.find(
'.')>=0
and name[-6:]!=
".local"):
253 if (name[-6:]==
".local"):
254 print (
"WARNING. Hostname is ",name,
" which does not define a domain. Consider setting $ATLAS_CONDDB to avoid this")
259 name=subprocess.check_output(
'hostname --fqdn').
decode(
'utf-8')
264 if (name.find(
'illegal')>-1
or name.find(
'unrecognized')>-1):
265 print (
"WARNING. hostname --fqdn returned the following:",name)
266 print (
"Consider setting $ATLAS_CONDDB to avoid this.")
273 print (
'AtlCoolLib tests')
276 print (
'Could not create test database')
278 spec=cool.RecordSpecification()
279 spec.extend(
'testString',cool.StorageType.String255)
280 folder=
ensureFolder(db,
'/TEST/TEST1',spec,
athenaDesc(
True,
'CondAttrListCollection'),cool.FolderVersioning.MULTI_VERSION)
282 print (
'Could not create test folder')
289 Class coolTool implements a base for python command-line tools to access
290 and set data in COOL folders
291 Incomplete baseclass implementation - clients must implement the following:
292 - setup(self,args) - set additional arguments
293 - usage(self) - print usage
294 - execute(self) - execute command
295 - procopts(self,opts) - (optional) - process additional command switches
297 def __init__(self,name,readonly,minarg,maxarg,longopts):
299 Initialise class and process command line options and switches
315 fullopts=longopts+[
'r=',
'rs=',
'ru=',
'l=',
'ls=',
'lu=',
'ts=',
'tu=',
'debug']
316 opts,args=getopt.getopt(sys.argv[1:],
'',fullopts)
317 except getopt.GetoptError
as e:
321 if len(args)<minarg
or len(args)>maxarg:
322 print (name,
'takes between',minarg,
'and',maxarg,
'non-optional parameters')
340 print (
'Usage:',self.
name,
' {<options>} dbname ',)
344 print (
'Options to specify IOV (default valid for all intervals)')
345 print (
'--rs=<first run>')
346 print (
'--ru=<last run> (inclusive)')
347 print (
'--r=<run> (only single run)')
348 print (
'--ls=<lumi block since>')
349 print (
'--l=<lumi block> (only single LB)')
350 print (
'--lu=<lumi block until> (inclusive)')
351 print (
'--ts=<initial timestamp> (in seconds)')
352 print (
'--tu=<final timestamp> (in seconds)')
353 print (
'--debug: Enable debugging information')
374 if (o==
'--debug'): self.
debug=
True
377 print (
'>== Data valid for run,LB [',self.
runmin,
',',self.
lumimin,
'] to [',self.
runmax,
',',self.
lumimax,
']')
386 print (
'ERROR in IOV definition: until<since')
395 """Hold a list of IOVs (start/end pairs) which are good, allowing parts
396 of list to be vetoed. start/end interpreted in COOL convention"""
398 "Initalise RangeList with given start and end (end=1 after last valid)"
403 "Veto part of the original range, splitting it if needed"
404 if (start>=end):
return
405 if (len(self.
_starts)==0):
return
416 elif (start<=self.
_ends[ix]
and end>=self.
_ends[ix]):
421 oldend=self.
_ends[ix]
428 """Return a list of tuples giving the allowed (start,end) within the
429 specified (start,end)"""
437 "Print representation of range as list of [x,y] allowed values"
444 "Remove the entry at idx"
449 "Put a new entry at idx, moving others to make space"
451 self.
_ends.insert(idx,end)
454 "Translation between timestamps and RunLB IOVs using /TRIGGER/LUMI/LBLB"
457 "Initialise and cache using the given DB connection, for RLB start/end"
468 lblbname=
'/TRIGGER/LUMI/LBLB'
470 readfolder=self.
readdb.getFolder(lblbname)
471 except Exception
as e:
473 print (
"Could not access folder %s " % lblbname)
474 raise RuntimeError (
"TimeStampToRLB initialisation error")
478 readobjs=readfolder.browseObjects(self.
since,self.
until,cool.ChannelSelection.all())
479 while readobjs.goToNext():
480 readobj=readobjs.currentRef()
481 payload=readobj.payload()
482 if (isFirst
is True):
486 self.
EndTime = payload[
'EndTime']
487 except Exception
as e:
489 print (
"Problem reading data from folder %s" % lblbname)
490 raise RuntimeError (
"TimeStampToRLB: initialisation error")
492 raise RuntimeError (
"TimeStampToRLB: no data for given runs")
495 lbtimename=
'/TRIGGER/LUMI/LBTIME'
497 readfolder=self.
readdb.getFolder(lbtimename)
498 except Exception
as e:
500 print (
"Problem accessing folder %s" % lbtimename)
501 raise RuntimeError (
"TimeStampToRLB: Initialisation error")
503 readobjs=readfolder.browseObjects(self.
StartTime, self.
EndTime, cool.ChannelSelection.all())
504 while readobjs.goToNext():
505 readobj=readobjs.currentRef()
506 payload=readobj.payload()
507 TimeStampStart = readobj.since()
508 TimeStampEnd = readobj.until()
509 iov=(payload[
'Run'] << 32)+payload[
'LumiBlock']
513 except Exception
as e:
515 print (
"Problem reading from folder %s" % lbtimename)
516 raise RuntimeError (
"TimeStampToRLB: Time data access error")
517 print (
"TimeStampToRLB initialised with %i entries in map" % len(self.
RLMap))
519 def getRLB(self,timestamp,StartType=True):
520 """Lookup a timestamp value. If it is outside a run, round up to next
521 run (StartType=True) or down to previous (StartType=False)"""
526 if (timestamp <= TSbegin):
533 if (timestamp >= TSend):
541 if __name__==
'__main__':