1 '''CMS Conditions DB Python library. 4 __author__ =
'Miguel Ojeda' 5 __copyright__ =
'Copyright 2013, CERN' 6 __credits__ = [
'Giacomo Govi',
'Miguel Ojeda',
'Andreas Pfeiffer']
7 __license__ =
'Unknown' 8 __maintainer__ =
'Giacomo Govi' 9 __email__ =
'mojedasa@cern.ch' 17 import sqlalchemy.ext.declarative
19 authPathEnvVar =
'COND_AUTH_PATH' 20 schema_name =
'CMS_CONDITIONS' 21 dbuser_name =
'cms_conditions' 22 dbreader_user_name =
'cms_cond_general_r' 23 dbwriter_user_name =
'cms_cond_general_w' 24 devdbwriter_user_name =
'cms_test_conditions' 25 logger = logging.getLogger(__name__)
33 ORAPRO =
'cms_orcon_adg' 36 ORAINT =
'cms_orcoff_int' 37 ORADEV =
'cms_orcoff_prep' 38 ONLINEORAPRO =
'cms_orcon_prod' 39 ONLINEORAINT =
'cmsintr_lb' 43 if logger.level == logging.NOTSET:
44 logger.setLevel(logging.WARN)
49 cls.
_members = sorted([member
for member
in dir(cls)
if not member.startswith(
'_')])
52 super(EnumMetaclass, cls).
__init__(name, bases, dct)
58 '''Returns the value for this key (if the key is an integer, 59 the value is the nth member from the sorted members list). 62 if isinstance(key, int):
68 '''Returns the key for this value. 75 '''A la PEP 435, simplified. 78 __metaclass__ = EnumMetaclass
83 return hashlib.sha1(data).hexdigest()
90 description_length = 4000
91 hash_length = len(
hash(
''))
93 web_experts_email =
'cms-cond-dev@cern.ch' 94 offline_db_experts_email =
'cms-offlinedb-exp@cern.ch' 95 offline_db_experts_phone =
'+41 22 76 70817, or 70817 from CERN; check https://twiki.cern.ch/twiki/bin/viewauth/CMS/DBShifterHelpPage if it does not work; availability depends on the state of the LHC' 97 contact_help =
'If you need assistance, please write an email to %s and %s. If you need immediate/urgent assistance, you can call the Offline DB expert on call (%s).' % (offline_db_experts_email, web_experts_email, offline_db_experts_phone)
99 The database parameter (--db) refers to the database where the tool 100 will connect to read all the data. By default, the production account 101 (through Frontier) will be used. 103 In subcommands which take a source and a destination, --db always refers to 104 the source, and --destdb to the destination. For both of them the following 107 The database parameter can be an official alias, a filename or any 108 valid SQLAlchemy URL. 110 The official aliases are the following strings (first column): 112 Alias Level Database RO/RW Notes 113 ------------ ----------- ------------- ---------- ------------------------------- 115 pro Production Frontier (ADG) read-only Default. 116 arc Archive Frontier read-only 117 int Integration Frontier read-only 118 dev Development Frontier read-only 119 boost Production Frontier read-only 120 boostprep Development Frontier read-only 122 orapro Production Oracle (ADG) read-only Password required. 123 oraarc Archive Oracle read-only Password required. 124 oraint Integration Oracle read-write Password required. 125 oradev Development Oracle read-write Password required. 127 onlineorapro Production Oracle read-write Password required. Online only. 128 onlineoraint Online Int Oracle read-write Password required. Online only. 130 Most of the time, if you are a regular user, you will want to read/copy 131 conditions from the Frontier production account. Therefore, you can omit 132 the --db parameter, unless you want to read from somewhere else, 133 e.g. from your local SQLite file. 135 In addition, the parameter may be a filename (path) pointing to a local 139 relative/path/to/file.db 140 /absolute/path/to/file.db 142 Finally, any valid SQLAlchemy URL can be used. This allows full 143 flexibility in cases where it may be needed, e.g. 145 sqlite:// In-memory, volatile SQLite DB. 146 oracle://user@devdb11 Your private Oracle DB in devdb11 [*] 148 [*] See https://account.cern.ch/ -> Services for more information 149 on personal Oracle accounts. 151 For the official aliases, the password will be asked automatically 152 interactively. The same applies for Oracle URLs where the password 153 was not provided inside it, e.g.: 155 oracle://user@devdb11 The tool will prompt you for the password. 156 oracle://user:pass@devdb11 Password inlined. [+] 158 [+] Caution: Never write passwords in command-line parameters in 159 multi-user machines (e.g. lxplus), since other users can see them 160 in the process table (e.g. ps). 162 This means that both the official aliases and the filenames are shortcuts 163 to the full SQLAlchemy URL equivalents, e.g. the following are the same: 165 relative/path/to/file.db === sqlite:///relative/path/to/file.db 166 /absolute/path/to/file.db === sqlite:////absolute/path/to/file.db 171 validation =
'validation' 189 _Base = sqlalchemy.ext.declarative.declarative_base()
193 if schema_name
is not None:
194 name =
'%s.%s' %(schema_name, table_name)
210 fqn =
'%s.%s' %(table, column)
211 if schema
is not None:
212 fqn =
'%s.%s' %(schema,fqn)
218 dbtype_name =
'%s_%s' %(baseType.__name__,backendName)
219 members[
'__tablename__'] = baseType.__tablename__
220 members[
'__table_args__'] =
None 221 if schemaName
is not None:
222 members[
'__table_args__'] = {
'schema': schemaName }
223 for k,v
in baseType.columns.items():
224 if isinstance(v[0],DbRef):
225 refColDbt = v[0].rtype.columns[v[0].rcol][0]
226 pk = (
True if v[1]==_Col.pk
else False)
228 members[k] = sqlalchemy.Column(refColDbt,sqlalchemy.ForeignKey(
fq_col(schemaName,v[0].rtype.__tablename__,v[0].rcol)),primary_key=
True)
230 nullable = (
False if v[1] == _Col.notNull
else True)
231 members[k] = sqlalchemy.Column(refColDbt,sqlalchemy.ForeignKey(
fq_col(schemaName,v[0].rtype.__tablename__,v[0].rcol)),nullable=nullable)
232 if v[0].rtype.__name__
not in deps_reg:
233 deps_reg.add(v[0].rtype.__name__)
234 reftype_name =
'%s_%s' %(v[0].rtype.__name__,backendName)
235 members[(v[0].rtype.__name__).lower()] = sqlalchemy.orm.relationship(reftype_name)
238 members[k] = sqlalchemy.Column(v[0],primary_key=
True)
240 nullable = (
True if v[1]==_Col.nullable
else False)
241 members[k] = sqlalchemy.Column(v[0],nullable=nullable)
242 dbType = type(dbtype_name,(_Base,),members)
244 if backendName
not in db_models.keys():
245 db_models[backendName] = {}
246 db_models[backendName][baseType.__name__] = dbType
250 if tp.__table_args__
is not None:
251 return tp.__table_args__[
'schema']
255 __tablename__ =
'TAG' 256 columns = {
'name': (sqlalchemy.String(name_length),_Col.pk),
257 'time_type': (sqlalchemy.Enum(*tuple(TimeType)),_Col.notNull),
258 'object_type': (sqlalchemy.String(name_length),_Col.notNull),
259 'synchronization': (sqlalchemy.Enum(*tuple(Synchronization)),_Col.notNull),
260 'description': (sqlalchemy.String(description_length),_Col.notNull),
261 'last_validated_time':(sqlalchemy.BIGINT,_Col.notNull),
262 'end_of_validity':(sqlalchemy.BIGINT,_Col.notNull),
263 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
264 'modification_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
268 __tablename__ =
'PAYLOAD' 269 columns = {
'hash': (sqlalchemy.CHAR(hash_length),_Col.pk),
270 'object_type': (sqlalchemy.String(name_length),_Col.notNull),
271 'data': (sqlalchemy.BLOB,_Col.notNull),
272 'streamer_info':(sqlalchemy.BLOB,_Col.notNull),
273 'version':(sqlalchemy.String(20),_Col.notNull),
274 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
278 __tablename__ =
'IOV' 279 columns = {
'tag_name':(
DbRef(Tag,
'name'),_Col.pk),
280 'since':(sqlalchemy.BIGINT,_Col.pk),
281 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.pk),
282 'payload_hash':(
DbRef(Payload,
'hash'),_Col.notNull) }
286 __tablename__ =
'GLOBAL_TAG' 287 columns = {
'name':(sqlalchemy.String(name_length),_Col.pk),
288 'validity': (sqlalchemy.BIGINT,_Col.notNull),
289 'description':(sqlalchemy.String(description_length),_Col.notNull),
290 'release':(sqlalchemy.String(name_length),_Col.notNull),
291 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
292 'snapshot_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
295 __tablename__ =
'GLOBAL_TAG_MAP' 296 columns = {
'global_tag_name':(
DbRef(GlobalTag,
'name'),_Col.pk),
297 'record':(sqlalchemy.String(name_length),_Col.pk),
298 'label':(sqlalchemy.String(name_length),_Col.pk),
299 'tag_name':(
DbRef(Tag,
'name'),_Col.notNull) }
304 __tablename__ =
'TAG_LOG' 305 columns = {
'tag_name':(
DbRef(Tag,
'name'),_Col.pk),
306 'event_time':(sqlalchemy.TIMESTAMP,_Col.pk),
307 'action':(sqlalchemy.String(100),_Col.pk),
308 'user_name':(sqlalchemy.String(100),_Col.notNull),
309 'host_name':(sqlalchemy.String(100),_Col.notNull),
310 'command':(sqlalchemy.String(500),_Col.notNull),
311 'user_text':(sqlalchemy.String(4000),_Col.notNull) }
314 __tablename__ =
'RUN_INFO' 315 columns = {
'run_number':(sqlalchemy.BIGINT,_Col.pk),
316 'start_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
317 'end_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
328 if url.drivername ==
'sqlite':
330 self.
engine = sqlalchemy.create_engine(url)
332 enabled_foreign_keys = self.engine.execute(
'pragma foreign_keys').
scalar()
333 supports_foreign_keys = enabled_foreign_keys
is not None 334 if not supports_foreign_keys:
335 logger.warning(
'Your SQLite database does not support foreign keys, so constraints will not be checked. Please upgrade.')
336 elif not enabled_foreign_keys:
337 self.engine.execute(
'pragma foreign_keys = on')
340 self.
engine = sqlalchemy.create_engine(url)
342 self.
_session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker(bind=self.
engine))
364 logging.debug(
' ... using db "%s", schema "%s"' % (url, self.
_schemaName) )
365 logging.debug(
'Loading db types...')
376 basename = theType.__name__
391 return _Base.metadata
414 '''Tests whether the current DB looks like a valid CMS Conditions one. 416 engine_connection = self.engine.connect()
421 ret =
all([self.engine.dialect.has_table(engine_connection, table.__tablename__,
getSchema(table))
for table
in [_Tag, _IOV, _Payload]])
422 engine_connection.close()
426 '''Initializes a database. 428 logging.info(
'Initializing database...')
430 logging.debug(
'Dropping tables...')
431 self.metadata.drop_all(self.
engine)
435 logging.debug(
'Creating tables...')
447 sessiondict[key %(session1._url.drivername,session1._url.host)] = session1
448 sessiondict[key %(session2._url.drivername,session2._url.host)] = session2
449 masterkey = key %(
'oracle',ONLINEORAPRO)
450 if masterkey
in sessiondict.keys():
451 return sessiondict[masterkey]
452 adgkey = key %(
'oracle',ORAPRO)
453 if adgkey
in sessiondict.keys():
454 return sessiondict[adgkey]
455 frontierkey = key %(
'frontier',PRO)
456 if frontierkey
in sessiondict.keys():
457 return sessiondict[frontierkey]
460 session = conn.session()
468 return subprocess.Popen([
'cmsGetFnConnect',
'frontier://%s' % database], stdout = subprocess.PIPE).
communicate()[0].
strip()
471 if technology ==
'frontier':
474 elif technology ==
'oracle':
475 return '%s://%s@%s' % (technology, schema_name, service)
479 if database.startswith(
'sqlite:')
or database.startswith(
'sqlite_file:'):
480 ignore, database = database.split(
':',1)
482 if ':' in database
and '://' not in database:
483 database, schema = database.split(
':')
487 'pro' : (
'frontier',
'PromptProd', {
'R': schema_name }, ), 488 'arc' : (
'frontier',
'FrontierArc', {
'R': schema_name }, ), 489 'int' : (
'frontier',
'FrontierInt', {
'R': schema_name }, ), 490 'dev' : (
'frontier',
'FrontierPrep', {
'R': schema_name }, ), 492 'orapro': (
'oracle',
'cms_orcon_adg', {
'R': dbreader_user_name }, ), 493 'oraarc': (
'oracle',
'cmsarc_lb', {
'R': dbreader_user_name }, ), 495 'oraint': (
'oracle',
'cms_orcoff_int', {
'R': dbreader_user_name, 496 'W': dbwriter_user_name }, ),
497 'oradev': (
'oracle',
'cms_orcoff_prep', {
'R': dbreader_user_name, 498 'W': devdbwriter_user_name }, ),
499 'onlineorapro': (
'oracle',
'cms_orcon_prod', {
'R': dbreader_user_name, 500 'W': dbwriter_user_name }, ),
501 'onlineoraint': (
'oracle',
'cmsintr_lb', {
'R': dbreader_user_name, 502 'W': dbwriter_user_name }, ),
505 if database
in officialdbs.keys():
506 key = (
'R' if read_only else 'W')
507 mapping = officialdbs[database]
510 schema_dict = mapping[2]
511 if key
in schema_dict.keys():
514 raise Exception(
"Read-only database %s://%s cannot be accessed in update mode." %(tech,service))
516 logging.debug(
'connection string set to "%s"' % database)
519 url = sqlalchemy.engine.url.make_url(database)
520 except sqlalchemy.exc.ArgumentError:
521 url = sqlalchemy.engine.url.make_url(
'sqlite:///%s' % database)
525 '''Returns a Connection instance to the CMS Condition DB. 527 See database_help for the description of the database parameter. 529 The verbosity level is as follows: 531 0 = No output (default). 532 1 = SQL statements issued, including their parameters. 533 2 = In addition, results of the queries (all rows and the column headers). 536 if url.drivername ==
'oracle':
537 if url.username
is None:
538 logging.error(
'Could not resolve the username for the connection %s. Please provide a connection in the format oracle://[user]:[pass]@[host]' %url )
539 raise Exception(
'Connection format error: %s' %url )
540 if url.password
is None:
542 if authPathEnvVar
in os.environ:
543 authPath = os.environ[authPathEnvVar]
545 if authPath
is not None:
546 authFile = os.path.join(authPath,
'.netrc')
547 if authFile
is not None:
548 entryKey = url.host.lower()+
"/"+url.username.lower()
549 logging.debug(
'Looking up credentials for %s in file %s ' %(entryKey,authFile) )
551 params = netrc.netrc( authFile ).authenticators(entryKey)
552 if params
is not None:
553 (username, account, password) = params
554 url.password = password
556 msg =
'The entry %s has not been found in the .netrc file.' %entryKey
560 pwd = getpass.getpass(
'Password for %s: ' %
str(url))
561 if pwd
is None or pwd ==
'':
562 pwd = getpass.getpass(
'Password for %s: ' %
str(url))
563 if pwd
is None or pwd ==
'':
564 raise Exception(
'Empty password provided, bailing out...')
568 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.INFO)
571 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.DEBUG)
579 ret = session.query(primary_key).\
580 filter(primary_key == value).\
582 except sqlalchemy.exc.OperationalError:
588 '''To be used inside filter(). 591 if timestamp
is None:
596 return sqlalchemy.literal(
True) == sqlalchemy.literal(
True)
598 return conddb.IOV.insertion_time <= _parse_timestamp(timestamp)
602 is_tag =
_exists(session, Tag.name, name)
605 result[
'type'] =
'Tag' 606 result[
'name'] = session.query(Tag).
get(name).name
607 result[
'timeType'] = session.query(Tag.time_type).\
608 filter(Tag.name == name).\
611 result[
'iovs'] = session.query(IOV.since, IOV.insertion_time, IOV.payload_hash, Payload.object_type).\
614 IOV.tag_name == name,
617 order_by(IOV.since.desc(), IOV.insertion_time.desc()).\
619 order_by(IOV.since, IOV.insertion_time).\
623 is_global_tag =
_exists(session, GlobalTag.name, name)
625 result[
'type'] =
'GlobalTag' 626 result[
'name'] = session.query(GlobalTag).
get(name)
627 result[
'tags'] = session.query(GlobalTagMap.record, GlobalTagMap.label, GlobalTagMap.tag_name).\
628 filter(GlobalTagMap.global_tag_name == name).\
629 order_by(GlobalTagMap.record, GlobalTagMap.label).\
631 except sqlalchemy.exc.OperationalError:
632 sys.stderr.write(
"No table for GlobalTags found in DB.\n\n")
634 if not is_tag
and not is_global_tag:
635 raise Exception(
'There is no tag or global tag named %s in the database.' % name)
641 data, payloadType = session.query(Payload.data, Payload.object_type).
filter(Payload.hash == hash).
one()
def init(self, drop=False)
def listObject(session, name, snapshot=None)
def getPayload(session, hash)
def getSessionOnMasterDB(session1, session2)
def _getCMSFrontierConnectionString(database)
static void * communicate(void *obj)
def make_url(database='pro', read_only=True)
def make_dbtype(backendName, schemaName, baseType)
def fq_col(schema, table, column)
def fq_name(schema_name, table_name)
static std::string join(char **cmd)
def connect(url, authPath=None, verbose=0)
def _getCMSSQLAlchemyConnectionString(technology, service, schema_name)
def get_dbtype(self, theType)
def _inserted_before(timestamp)
def __init__(self, refType, refColumn)
T get(const Candidate &c)
def _exists(session, primary_key, value)
double scalar(const CLHEP::HepGenMatrix &m)
Return the matrix as a scalar. Raise an assertion if the matris is not .