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_cond_general_w' 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) }
267 __tablename__ =
'TAG_METADATA' 268 columns = {
'tag_name': (
DbRef(Tag,
'name'),_Col.pk),
269 'min_serialization_v': (sqlalchemy.String(20),_Col.notNull),
270 'min_since': (sqlalchemy.BIGINT,_Col.notNull),
271 'modification_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
274 __tablename__ =
'PAYLOAD' 275 columns = {
'hash': (sqlalchemy.CHAR(hash_length),_Col.pk),
276 'object_type': (sqlalchemy.String(name_length),_Col.notNull),
277 'data': (sqlalchemy.BLOB,_Col.notNull),
278 'streamer_info':(sqlalchemy.BLOB,_Col.notNull),
279 'version':(sqlalchemy.String(20),_Col.notNull),
280 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
284 __tablename__ =
'IOV' 285 columns = {
'tag_name':(
DbRef(Tag,
'name'),_Col.pk),
286 'since':(sqlalchemy.BIGINT,_Col.pk),
287 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.pk),
288 'payload_hash':(
DbRef(Payload,
'hash'),_Col.notNull) }
292 __tablename__ =
'GLOBAL_TAG' 293 columns = {
'name':(sqlalchemy.String(name_length),_Col.pk),
294 'validity': (sqlalchemy.BIGINT,_Col.notNull),
295 'description':(sqlalchemy.String(description_length),_Col.notNull),
296 'release':(sqlalchemy.String(name_length),_Col.notNull),
297 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
298 'snapshot_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
301 __tablename__ =
'GLOBAL_TAG_MAP' 302 columns = {
'global_tag_name':(
DbRef(GlobalTag,
'name'),_Col.pk),
303 'record':(sqlalchemy.String(name_length),_Col.pk),
304 'label':(sqlalchemy.String(name_length),_Col.pk),
305 'tag_name':(
DbRef(Tag,
'name'),_Col.notNull) }
310 __tablename__ =
'TAG_LOG' 311 columns = {
'tag_name':(
DbRef(Tag,
'name'),_Col.pk),
312 'event_time':(sqlalchemy.TIMESTAMP,_Col.pk),
313 'action':(sqlalchemy.String(100),_Col.pk),
314 'user_name':(sqlalchemy.String(100),_Col.notNull),
315 'host_name':(sqlalchemy.String(100),_Col.notNull),
316 'command':(sqlalchemy.String(500),_Col.notNull),
317 'user_text':(sqlalchemy.String(4000),_Col.notNull) }
320 __tablename__ =
'RUN_INFO' 321 columns = {
'run_number':(sqlalchemy.BIGINT,_Col.pk),
322 'start_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
323 'end_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
326 __tablename__ =
'BOOST_RUN_MAP' 327 columns = {
'run_number':(sqlalchemy.BIGINT,_Col.pk),
328 'run_start_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
329 'boost_version': (sqlalchemy.String(20),_Col.notNull) }
339 if url.drivername ==
'sqlite':
341 self.
engine = sqlalchemy.create_engine(url)
343 enabled_foreign_keys = self.engine.execute(
'pragma foreign_keys').
scalar()
344 supports_foreign_keys = enabled_foreign_keys
is not None 345 if not supports_foreign_keys:
346 logger.warning(
'Your SQLite database does not support foreign keys, so constraints will not be checked. Please upgrade.')
347 elif not enabled_foreign_keys:
348 self.engine.execute(
'pragma foreign_keys = on')
351 self.
engine = sqlalchemy.create_engine(url)
353 self.
_session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker(bind=self.
engine))
375 logging.debug(
' ... using db "%s", schema "%s"' % (url, self.
_schemaName) )
376 logging.debug(
'Loading db types...')
390 basename = theType.__name__
406 return _Base.metadata
429 '''Tests whether the current DB looks like a valid CMS Conditions one. 431 engine_connection = self.engine.connect()
436 ret =
all([self.engine.dialect.has_table(engine_connection, table.__tablename__,
getSchema(table))
for table
in [_Tag, _IOV, _Payload]])
437 engine_connection.close()
441 '''Initializes a database. 443 logging.info(
'Initializing database...')
445 logging.debug(
'Dropping tables...')
446 self.metadata.drop_all(self.
engine)
450 logging.debug(
'Creating tables...')
462 sessiondict[key %(session1._url.drivername,session1._url.host)] = session1
463 sessiondict[key %(session2._url.drivername,session2._url.host)] = session2
464 masterkey = key %(
'oracle',ONLINEORAPRO)
465 if masterkey
in sessiondict.keys():
466 return sessiondict[masterkey]
467 adgkey = key %(
'oracle',ORAPRO)
468 if adgkey
in sessiondict.keys():
469 return sessiondict[adgkey]
470 frontierkey = key %(
'frontier',PRO)
471 if frontierkey
in sessiondict.keys():
472 return sessiondict[frontierkey]
475 session = conn.session()
483 return subprocess.Popen([
'cmsGetFnConnect',
'frontier://%s' % database], stdout = subprocess.PIPE).
communicate()[0].
strip()
486 if technology ==
'frontier':
489 elif technology ==
'oracle':
490 return '%s://%s@%s' % (technology, schema_name, service)
494 if database.startswith(
'sqlite:')
or database.startswith(
'sqlite_file:'):
495 ignore, database = database.split(
':',1)
497 if ':' in database
and '://' not in database:
498 database, schema = database.split(
':')
502 'pro' : (
'frontier',
'PromptProd', {
'R': schema_name }, ), 503 'arc' : (
'frontier',
'FrontierArc', {
'R': schema_name }, ), 504 'int' : (
'frontier',
'FrontierInt', {
'R': schema_name }, ), 505 'dev' : (
'frontier',
'FrontierPrep', {
'R': schema_name }, ), 507 'orapro': (
'oracle',
'cms_orcon_adg', {
'R': dbreader_user_name }, ), 508 'oraarc': (
'oracle',
'cmsarc_lb', {
'R': dbreader_user_name }, ), 510 'oraint': (
'oracle',
'cms_orcoff_int', {
'R': dbreader_user_name, 511 'W': dbwriter_user_name }, ),
512 'oradev': (
'oracle',
'cms_orcoff_prep', {
'R': dbreader_user_name, 513 'W': devdbwriter_user_name }, ),
514 'onlineorapro': (
'oracle',
'cms_orcon_prod', {
'R': dbreader_user_name, 515 'W': dbwriter_user_name }, ),
516 'onlineoraint': (
'oracle',
'cmsintr_lb', {
'R': dbreader_user_name, 517 'W': dbwriter_user_name }, ),
520 if database
in officialdbs.keys():
521 key = (
'R' if read_only else 'W')
522 mapping = officialdbs[database]
525 schema_dict = mapping[2]
526 if key
in schema_dict.keys():
529 raise Exception(
"Read-only database %s://%s cannot be accessed in update mode." %(tech,service))
531 logging.debug(
'connection string set to "%s"' % database)
534 url = sqlalchemy.engine.url.make_url(database)
535 except sqlalchemy.exc.ArgumentError:
536 url = sqlalchemy.engine.url.make_url(
'sqlite:///%s' % database)
540 '''Returns a Connection instance to the CMS Condition DB. 542 See database_help for the description of the database parameter. 544 The verbosity level is as follows: 546 0 = No output (default). 547 1 = SQL statements issued, including their parameters. 548 2 = In addition, results of the queries (all rows and the column headers). 551 if url.drivername ==
'oracle':
552 if url.username
is None:
553 logging.error(
'Could not resolve the username for the connection %s. Please provide a connection in the format oracle://[user]:[pass]@[host]' %url )
554 raise Exception(
'Connection format error: %s' %url )
555 if url.password
is None:
557 if authPathEnvVar
in os.environ:
558 authPath = os.environ[authPathEnvVar]
560 if authPath
is not None:
561 authFile = os.path.join(authPath,
'.netrc')
562 if authFile
is not None:
563 entryKey = url.host.lower()+
"/"+url.username.lower()
564 logging.debug(
'Looking up credentials for %s in file %s ' %(entryKey,authFile) )
566 params = netrc.netrc( authFile ).authenticators(entryKey)
567 if params
is not None:
568 (username, account, password) = params
569 url.password = password
571 msg =
'The entry %s has not been found in the .netrc file.' %entryKey
575 pwd = getpass.getpass(
'Password for %s: ' %
str(url))
576 if pwd
is None or pwd ==
'':
577 pwd = getpass.getpass(
'Password for %s: ' %
str(url))
578 if pwd
is None or pwd ==
'':
579 raise Exception(
'Empty password provided, bailing out...')
583 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.INFO)
586 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.DEBUG)
594 ret = session.query(primary_key).\
595 filter(primary_key == value).\
597 except sqlalchemy.exc.OperationalError:
603 '''To be used inside filter(). 606 if timestamp
is None:
611 return sqlalchemy.literal(
True) == sqlalchemy.literal(
True)
613 return conddb.IOV.insertion_time <= _parse_timestamp(timestamp)
617 is_tag =
_exists(session, Tag.name, name)
620 result[
'type'] =
'Tag' 621 result[
'name'] = session.query(Tag).
get(name).name
622 result[
'timeType'] = session.query(Tag.time_type).\
623 filter(Tag.name == name).\
626 result[
'iovs'] = session.query(IOV.since, IOV.insertion_time, IOV.payload_hash, Payload.object_type).\
629 IOV.tag_name == name,
632 order_by(IOV.since.desc(), IOV.insertion_time.desc()).\
634 order_by(IOV.since, IOV.insertion_time).\
638 is_global_tag =
_exists(session, GlobalTag.name, name)
640 result[
'type'] =
'GlobalTag' 641 result[
'name'] = session.query(GlobalTag).
get(name)
642 result[
'tags'] = session.query(GlobalTagMap.record, GlobalTagMap.label, GlobalTagMap.tag_name).\
643 filter(GlobalTagMap.global_tag_name == name).\
644 order_by(GlobalTagMap.record, GlobalTagMap.label).\
646 except sqlalchemy.exc.OperationalError:
647 sys.stderr.write(
"No table for GlobalTags found in DB.\n\n")
649 if not is_tag
and not is_global_tag:
650 raise Exception(
'There is no tag or global tag named %s in the database.' % name)
656 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 .