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 'modification_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
273 __tablename__ =
'PAYLOAD' 274 columns = {
'hash': (sqlalchemy.CHAR(hash_length),_Col.pk),
275 'object_type': (sqlalchemy.String(name_length),_Col.notNull),
276 'data': (sqlalchemy.BLOB,_Col.notNull),
277 'streamer_info':(sqlalchemy.BLOB,_Col.notNull),
278 'version':(sqlalchemy.String(20),_Col.notNull),
279 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
283 __tablename__ =
'IOV' 284 columns = {
'tag_name':(
DbRef(Tag,
'name'),_Col.pk),
285 'since':(sqlalchemy.BIGINT,_Col.pk),
286 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.pk),
287 'payload_hash':(
DbRef(Payload,
'hash'),_Col.notNull) }
291 __tablename__ =
'GLOBAL_TAG' 292 columns = {
'name':(sqlalchemy.String(name_length),_Col.pk),
293 'validity': (sqlalchemy.BIGINT,_Col.notNull),
294 'description':(sqlalchemy.String(description_length),_Col.notNull),
295 'release':(sqlalchemy.String(name_length),_Col.notNull),
296 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
297 'snapshot_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
300 __tablename__ =
'GLOBAL_TAG_MAP' 301 columns = {
'global_tag_name':(
DbRef(GlobalTag,
'name'),_Col.pk),
302 'record':(sqlalchemy.String(name_length),_Col.pk),
303 'label':(sqlalchemy.String(name_length),_Col.pk),
304 'tag_name':(
DbRef(Tag,
'name'),_Col.notNull) }
309 __tablename__ =
'TAG_LOG' 310 columns = {
'tag_name':(
DbRef(Tag,
'name'),_Col.pk),
311 'event_time':(sqlalchemy.TIMESTAMP,_Col.pk),
312 'action':(sqlalchemy.String(100),_Col.pk),
313 'user_name':(sqlalchemy.String(100),_Col.notNull),
314 'host_name':(sqlalchemy.String(100),_Col.notNull),
315 'command':(sqlalchemy.String(500),_Col.notNull),
316 'user_text':(sqlalchemy.String(4000),_Col.notNull) }
319 __tablename__ =
'RUN_INFO' 320 columns = {
'run_number':(sqlalchemy.BIGINT,_Col.pk),
321 'start_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
322 'end_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
325 __tablename__ =
'BOOST_RUN_MAP' 326 columns = {
'run_number':(sqlalchemy.BIGINT,_Col.pk),
327 'run_start_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
328 'boost_version': (sqlalchemy.String(20),_Col.notNull) }
338 if url.drivername ==
'sqlite':
340 self.
engine = sqlalchemy.create_engine(url)
342 enabled_foreign_keys = self.engine.execute(
'pragma foreign_keys').
scalar()
343 supports_foreign_keys = enabled_foreign_keys
is not None 344 if not supports_foreign_keys:
345 logger.warning(
'Your SQLite database does not support foreign keys, so constraints will not be checked. Please upgrade.')
346 elif not enabled_foreign_keys:
347 self.engine.execute(
'pragma foreign_keys = on')
350 self.
engine = sqlalchemy.create_engine(url)
352 self.
_session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker(bind=self.
engine))
374 logging.debug(
' ... using db "%s", schema "%s"' % (url, self.
_schemaName) )
375 logging.debug(
'Loading db types...')
389 basename = theType.__name__
405 return _Base.metadata
428 '''Tests whether the current DB looks like a valid CMS Conditions one. 430 engine_connection = self.engine.connect()
435 ret =
all([self.engine.dialect.has_table(engine_connection, table.__tablename__,
getSchema(table))
for table
in [_Tag, _IOV, _Payload]])
436 engine_connection.close()
440 '''Initializes a database. 442 logging.info(
'Initializing database...')
444 logging.debug(
'Dropping tables...')
445 self.metadata.drop_all(self.
engine)
449 logging.debug(
'Creating tables...')
461 sessiondict[key %(session1._url.drivername,session1._url.host)] = session1
462 sessiondict[key %(session2._url.drivername,session2._url.host)] = session2
463 masterkey = key %(
'oracle',ONLINEORAPRO)
464 if masterkey
in sessiondict.keys():
465 return sessiondict[masterkey]
466 adgkey = key %(
'oracle',ORAPRO)
467 if adgkey
in sessiondict.keys():
468 return sessiondict[adgkey]
469 frontierkey = key %(
'frontier',PRO)
470 if frontierkey
in sessiondict.keys():
471 return sessiondict[frontierkey]
474 session = conn.session()
482 return subprocess.Popen([
'cmsGetFnConnect',
'frontier://%s' % database], stdout = subprocess.PIPE).
communicate()[0].
strip()
485 if technology ==
'frontier':
488 elif technology ==
'oracle':
489 return '%s://%s@%s' % (technology, schema_name, service)
493 if database.startswith(
'sqlite:')
or database.startswith(
'sqlite_file:'):
494 ignore, database = database.split(
':',1)
496 if ':' in database
and '://' not in database:
497 database, schema = database.split(
':')
501 'pro' : (
'frontier',
'PromptProd', {
'R': schema_name }, ), 502 'arc' : (
'frontier',
'FrontierArc', {
'R': schema_name }, ), 503 'int' : (
'frontier',
'FrontierInt', {
'R': schema_name }, ), 504 'dev' : (
'frontier',
'FrontierPrep', {
'R': schema_name }, ), 506 'orapro': (
'oracle',
'cms_orcon_adg', {
'R': dbreader_user_name }, ), 507 'oraarc': (
'oracle',
'cmsarc_lb', {
'R': dbreader_user_name }, ), 509 'oraint': (
'oracle',
'cms_orcoff_int', {
'R': dbreader_user_name, 510 'W': dbwriter_user_name }, ),
511 'oradev': (
'oracle',
'cms_orcoff_prep', {
'R': dbreader_user_name, 512 'W': devdbwriter_user_name }, ),
513 'onlineorapro': (
'oracle',
'cms_orcon_prod', {
'R': dbreader_user_name, 514 'W': dbwriter_user_name }, ),
515 'onlineoraint': (
'oracle',
'cmsintr_lb', {
'R': dbreader_user_name, 516 'W': dbwriter_user_name }, ),
519 if database
in officialdbs.keys():
520 key = (
'R' if read_only else 'W')
521 mapping = officialdbs[database]
524 schema_dict = mapping[2]
525 if key
in schema_dict.keys():
528 raise Exception(
"Read-only database %s://%s cannot be accessed in update mode." %(tech,service))
530 logging.debug(
'connection string set to "%s"' % database)
533 url = sqlalchemy.engine.url.make_url(database)
534 except sqlalchemy.exc.ArgumentError:
535 url = sqlalchemy.engine.url.make_url(
'sqlite:///%s' % database)
539 '''Returns a Connection instance to the CMS Condition DB. 541 See database_help for the description of the database parameter. 543 The verbosity level is as follows: 545 0 = No output (default). 546 1 = SQL statements issued, including their parameters. 547 2 = In addition, results of the queries (all rows and the column headers). 550 if url.drivername ==
'oracle':
551 if url.username
is None:
552 logging.error(
'Could not resolve the username for the connection %s. Please provide a connection in the format oracle://[user]:[pass]@[host]' %url )
553 raise Exception(
'Connection format error: %s' %url )
554 if url.password
is None:
556 if authPathEnvVar
in os.environ:
557 authPath = os.environ[authPathEnvVar]
559 if authPath
is not None:
560 authFile = os.path.join(authPath,
'.netrc')
561 if authFile
is not None:
562 entryKey = url.host.lower()+
"/"+url.username.lower()
563 logging.debug(
'Looking up credentials for %s in file %s ' %(entryKey,authFile) )
565 params = netrc.netrc( authFile ).authenticators(entryKey)
566 if params
is not None:
567 (username, account, password) = params
568 url.password = password
570 msg =
'The entry %s has not been found in the .netrc file.' %entryKey
574 pwd = getpass.getpass(
'Password for %s: ' %
str(url))
575 if pwd
is None or pwd ==
'':
576 pwd = getpass.getpass(
'Password for %s: ' %
str(url))
577 if pwd
is None or pwd ==
'':
578 raise Exception(
'Empty password provided, bailing out...')
582 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.INFO)
585 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.DEBUG)
593 ret = session.query(primary_key).\
594 filter(primary_key == value).\
596 except sqlalchemy.exc.OperationalError:
602 '''To be used inside filter(). 605 if timestamp
is None:
610 return sqlalchemy.literal(
True) == sqlalchemy.literal(
True)
612 return conddb.IOV.insertion_time <= _parse_timestamp(timestamp)
616 is_tag =
_exists(session, Tag.name, name)
619 result[
'type'] =
'Tag' 620 result[
'name'] = session.query(Tag).
get(name).name
621 result[
'timeType'] = session.query(Tag.time_type).\
622 filter(Tag.name == name).\
625 result[
'iovs'] = session.query(IOV.since, IOV.insertion_time, IOV.payload_hash, Payload.object_type).\
628 IOV.tag_name == name,
631 order_by(IOV.since.desc(), IOV.insertion_time.desc()).\
633 order_by(IOV.since, IOV.insertion_time).\
637 is_global_tag =
_exists(session, GlobalTag.name, name)
639 result[
'type'] =
'GlobalTag' 640 result[
'name'] = session.query(GlobalTag).
get(name)
641 result[
'tags'] = session.query(GlobalTagMap.record, GlobalTagMap.label, GlobalTagMap.tag_name).\
642 filter(GlobalTagMap.global_tag_name == name).\
643 order_by(GlobalTagMap.record, GlobalTagMap.label).\
645 except sqlalchemy.exc.OperationalError:
646 sys.stderr.write(
"No table for GlobalTags found in DB.\n\n")
648 if not is_tag
and not is_global_tag:
649 raise Exception(
'There is no tag or global tag named %s in the database.' % name)
655 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 .