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__ =
'Miguel Ojeda'
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__)
29 if logger.level == logging.NOTSET:
30 logger.setLevel(logging.WARN)
35 cls._members = sorted([member
for member
in dir(cls)
if not member.startswith(
'_')])
36 cls._map =
dict([(member, getattr(cls, member))
for member
in cls._members])
37 cls._reversemap =
dict([(value, key)
for (key, value)
in cls._map.items()])
38 super(EnumMetaclass, cls).
__init__(name, bases, dct)
41 return len(cls._members)
44 '''Returns the value for this key (if the key is an integer,
45 the value is the nth member from the sorted members list).
48 if isinstance(key, int):
50 key = cls._members[key]
54 '''Returns the key for this value.
57 return cls._reversemap[value]
61 '''A la PEP 435, simplified.
64 __metaclass__ = EnumMetaclass
69 return hashlib.sha1(data).hexdigest()
76 description_length = 4000
77 hash_length = len(
hash(
''))
79 web_experts_email =
'cms-cond-dev@cern.ch'
80 offline_db_experts_email =
'cms-offlinedb-exp@cern.ch'
81 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'
83 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)
85 The database parameter (--db) refers to the database where the tool
86 will connect to read all the data. By default, the production account
87 (through Frontier) will be used.
89 In subcommands which take a source and a destination, --db always refers to
90 the source, and --destdb to the destination. For both of them the following
93 The database parameter can be an official alias, a filename or any
96 The official aliases are the following strings (first column):
98 Alias Level Database RO/RW Notes
99 ------------ ----------- ------------- ---------- -------------------------------
101 pro Production Frontier (ADG) read-only Default.
102 arc Archive Frontier read-only
103 int Integration Frontier read-only
104 dev Development Frontier read-only
105 boost Production Frontier read-only
106 boostprep Development Frontier read-only
108 orapro Production Oracle (ADG) read-only Password required.
109 oraarc Archive Oracle read-only Password required.
110 oraint Integration Oracle read-write Password required.
111 oradev Development Oracle read-write Password required.
113 onlineorapro Production Oracle read-write Password required. Online only.
114 onlineoraint Online Int Oracle read-write Password required. Online only.
116 Most of the time, if you are a regular user, you will want to read/copy
117 conditions from the Frontier production account. Therefore, you can omit
118 the --db parameter, unless you want to read from somewhere else,
119 e.g. from your local SQLite file.
121 In addition, the parameter may be a filename (path) pointing to a local
125 relative/path/to/file.db
126 /absolute/path/to/file.db
128 Finally, any valid SQLAlchemy URL can be used. This allows full
129 flexibility in cases where it may be needed, e.g.
131 sqlite:// In-memory, volatile SQLite DB.
132 oracle://user@devdb11 Your private Oracle DB in devdb11 [*]
134 [*] See https://account.cern.ch/ -> Services for more information
135 on personal Oracle accounts.
137 For the official aliases, the password will be asked automatically
138 interactively. The same applies for Oracle URLs where the password
139 was not provided inside it, e.g.:
141 oracle://user@devdb11 The tool will prompt you for the password.
142 oracle://user:pass@devdb11 Password inlined. [+]
144 [+] Caution: Never write passwords in command-line parameters in
145 multi-user machines (e.g. lxplus), since other users can see them
146 in the process table (e.g. ps).
148 This means that both the official aliases and the filenames are shortcuts
149 to the full SQLAlchemy URL equivalents, e.g. the following are the same:
151 relative/path/to/file.db === sqlite:///relative/path/to/file.db
152 /absolute/path/to/file.db === sqlite:////absolute/path/to/file.db
157 validation =
'validation'
175 _Base = sqlalchemy.ext.declarative.declarative_base()
179 if schema_name
is not None:
180 name =
'%s.%s' %(schema_name, table_name)
198 fqn =
'%s.%s' %(table, column)
199 if schema
is not None:
200 fqn =
'%s.%s' %(schema,fqn)
206 dbtype_name =
'%s_%s' %(baseType.__name__,backendName)
207 members[
'__tablename__'] = baseType.__tablename__
208 members[
'__table_args__'] =
None
209 if schemaName
is not None:
210 members[
'__table_args__'] = {
'schema': schemaName }
211 for k,v
in baseType.columns.items():
212 if isinstance(v[0],DbRef):
213 refColDbt = v[0].rtype.columns[v[0].rcol][0]
214 pk = (
True if v[1]==_Col.pk
else False)
216 members[k] = sqlalchemy.Column(refColDbt,sqlalchemy.ForeignKey(
fq_col(schemaName,v[0].rtype.__tablename__,v[0].rcol)),primary_key=
True)
218 nullable = (
False if v[1] == _Col.notNull
else True)
219 members[k] = sqlalchemy.Column(refColDbt,sqlalchemy.ForeignKey(
fq_col(schemaName,v[0].rtype.__tablename__,v[0].rcol)),nullable=nullable)
220 if v[0].rtype.__name__
not in deps_reg:
221 deps_reg.add(v[0].rtype.__name__)
222 reftype_name =
'%s_%s' %(v[0].rtype.__name__,backendName)
223 members[(v[0].rtype.__name__).lower()] = sqlalchemy.orm.relationship(reftype_name)
226 members[k] = sqlalchemy.Column(v[0],primary_key=
True)
228 nullable = (
True if v[1]==_Col.nullable
else False)
229 members[k] = sqlalchemy.Column(v[0],nullable=nullable)
230 dbType = type(dbtype_name,(_Base,),members)
232 if backendName
not in db_models.keys():
233 db_models[backendName] = {}
234 db_models[backendName][baseType.__name__] = dbType
238 if tp.__table_args__
is not None:
239 return tp.__table_args__[
'schema']
246 __tablename__ =
'TAG'
247 columns = {
'name': (sqlalchemy.String(name_length),_Col.pk),
248 'time_type': (sqlalchemy.Enum(*tuple(TimeType)),_Col.notNull),
249 'object_type': (sqlalchemy.String(name_length),_Col.notNull),
250 'synchronization': (sqlalchemy.Enum(*tuple(Synchronization)),_Col.notNull),
251 'description': (sqlalchemy.String(description_length),_Col.notNull),
252 'last_validated_time':(sqlalchemy.BIGINT,_Col.notNull),
253 'end_of_validity':(sqlalchemy.BIGINT,_Col.notNull),
254 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
255 'modification_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
259 __tablename__ =
'PAYLOAD'
260 columns = {
'hash': (sqlalchemy.CHAR(hash_length),_Col.pk),
261 'object_type': (sqlalchemy.String(name_length),_Col.notNull),
262 'data': (sqlalchemy.BLOB,_Col.notNull),
263 'streamer_info':(sqlalchemy.BLOB,_Col.notNull),
264 'version':(sqlalchemy.String(20),_Col.notNull),
265 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
269 __tablename__ =
'IOV'
270 columns = {
'tag_name':(
DbRef(Tag,
'name'),_Col.pk),
271 'since':(sqlalchemy.BIGINT,_Col.pk),
272 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.pk),
273 'payload_hash':(
DbRef(Payload,
'hash'),_Col.pk) }
279 __tablename__ =
'global_tag'
280 columns = {
'name':(sqlalchemy.String(name_length),_Col.pk),
281 'validity': (sqlalchemy.BIGINT,_Col.notNull),
282 'description':(sqlalchemy.String(description_length),_Col.notNull),
283 'release':(sqlalchemy.String(name_length),_Col.notNull),
284 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
285 'snapshot_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
288 __tablename__ =
'global_tag_map'
289 columns = {
'global_tag_name':(
DbRef(GlobalTag,
'name'),_Col.pk),
290 'record':(sqlalchemy.String(name_length),_Col.pk),
291 'label':(sqlalchemy.String(name_length),_Col.pk),
292 'tag_name':(
DbRef(Tag,
'name'),_Col.notNull) }
297 __tablename__ =
'TAG_LOG'
298 columns = {
'tag_name':(
DbRef(Tag,
'name'),_Col.pk),
299 'event_time':(sqlalchemy.TIMESTAMP,_Col.pk),
300 'action':(sqlalchemy.String(100),_Col.pk),
301 'user_name':(sqlalchemy.String(100),_Col.notNull),
302 'host_name':(sqlalchemy.String(100),_Col.notNull),
303 'command':(sqlalchemy.String(500),_Col.notNull),
304 'user_text':(sqlalchemy.String(4000),_Col.notNull) }
315 if url.drivername ==
'sqlite':
321 self.
engine = sqlalchemy.create_engine(url)
323 enabled_foreign_keys = self.engine.execute(
'pragma foreign_keys').
scalar()
324 supports_foreign_keys = enabled_foreign_keys
is not None
325 if not supports_foreign_keys:
326 logger.warning(
'Your SQLite database does not support foreign keys, so constraints will not be checked. Please upgrade.')
327 elif not enabled_foreign_keys:
328 self.engine.execute(
'pragma foreign_keys = on')
331 self.
engine = sqlalchemy.create_engine(url)
333 self.
_session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker(bind=self.
engine))
355 logging.debug(
' ... using db "%s", schema "%s"' % (url, self.
_schemaName) )
356 logging.debug(
'Loading db types...')
365 basename = theType.__name__
379 return _Base.metadata
402 '''Tests whether the current DB looks like a valid CMS Conditions one.
404 engine_connection = self.engine.connect()
411 ret =
all([self.engine.dialect.has_table(engine_connection, table.__tablename__,
getSchema(table))
for table
in [_Tag, _IOV, _Payload]])
412 engine_connection.close()
416 '''Initializes a database.
418 logging.info(
'Initializing database...')
420 logging.debug(
'Dropping tables...')
421 self.metadata.drop_all(self.
engine)
424 raise Exception(
'Looks like the database is already a valid CMS Conditions one.')
426 logging.debug(
'Creating tables...')
438 conn = sqlite3.connect( self._url.database )
440 stmt = string.Template(
'ALTER TABLE $before RENAME TO $after')
441 c.execute( stmt.substitute( before=GlobalTag.__tablename__, after=
'TMP0' ) )
442 c.execute( stmt.substitute( before=
'TMP0', after=GlobalTag.__tablename__.upper() ) )
443 c.execute( stmt.substitute( before=GlobalTagMap.__tablename__, after=
'TMP1' ) )
444 c.execute( stmt.substitute( before=
'TMP1', after=GlobalTagMap.__tablename__.upper() ) )
454 return subprocess.Popen([
'cmsGetFnConnect',
'frontier://%s' % database], stdout = subprocess.PIPE).
communicate()[0].strip()
463 return 'oracle://%s@%s' % (schema, database)
471 if ':' in database
and '://' not in database:
472 database, schema = database.split(
':')
494 key = database + (
'_R' if read_only
else '_W')
496 database = mapping[key]()
498 logging.debug(
'connection string set to "%s"' % database)
501 url = sqlalchemy.engine.url.make_url(database)
502 except sqlalchemy.exc.ArgumentError:
503 url = sqlalchemy.engine.url.make_url(
'sqlite:///%s' % database)
506 def connect(url, init=False, authPath=None, verbose=0):
507 '''Returns a Connection instance to the CMS Condition DB.
509 See database_help for the description of the database parameter.
511 The verbosity level is as follows:
513 0 = No output (default).
514 1 = SQL statements issued, including their parameters.
515 2 = In addition, results of the queries (all rows and the column headers).
518 if url.drivername ==
'oracle' and url.password
is None:
520 if authPathEnvVar
in os.environ:
521 authPath = os.environ[authPathEnvVar]
523 if authPath
is not None:
524 authFile = os.path.join(authPath,
'.netrc')
525 if authFile
is not None:
526 entryKey = url.host+
"/"+url.username
527 logging.debug(
'Looking up credentials for %s in file %s ' %(entryKey,authFile) )
531 (username, account, password) = netrc.netrc( authFile ).authenticators(entryKey)
532 url.password = password
534 logging.error(
'.netrc file expected in %s has not been found or cannot be open.' %authPath)
536 except TypeError
as e:
537 logging.error(
'The .netrc file in %s is invalid, or the targeted entry has not been found.' %authPath)
538 except Exception
as e:
539 logging.error(
'Problem with .netrc file in %s: %s' %(authPath,str(e)))
542 url.password = getpass.getpass(
'Password for %s: ' % str(url))
545 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.INFO)
548 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.DEBUG)
556 ret = session.query(primary_key).\
557 filter(primary_key == value).\
559 except sqlalchemy.exc.OperationalError:
565 '''To be used inside filter().
568 if timestamp
is None:
573 return sqlalchemy.literal(
True) == sqlalchemy.literal(
True)
575 return conddb.IOV.insertion_time <= _parse_timestamp(timestamp)
579 is_tag =
_exists(session, Tag.name, name)
582 result[
'type'] =
'Tag'
583 result[
'name'] = session.query(Tag).
get(name).name
584 result[
'timeType'] = session.query(Tag.time_type).\
585 filter(Tag.name == name).\
588 result[
'iovs'] = session.query(IOV.since, IOV.insertion_time, IOV.payload_hash, Payload.object_type).\
591 IOV.tag_name == name,
594 order_by(IOV.since.desc(), IOV.insertion_time.desc()).\
596 order_by(IOV.since, IOV.insertion_time).\
600 is_global_tag =
_exists(session, GlobalTag.name, name)
602 result[
'type'] =
'GlobalTag'
603 result[
'name'] = session.query(GlobalTag).
get(name)
604 result[
'tags'] = session.query(GlobalTagMap.record, GlobalTagMap.label, GlobalTagMap.tag_name).\
605 filter(GlobalTagMap.global_tag_name == name).\
606 order_by(GlobalTagMap.record, GlobalTagMap.label).\
608 except sqlalchemy.exc.OperationalError:
609 sys.stderr.write(
"No table for GlobalTags found in DB.\n\n")
611 if not is_tag
and not is_global_tag:
612 raise Exception(
'There is no tag or global tag named %s in the database.' % name)
618 data, payloadType = session.query(Payload.data, Payload.object_type).
filter(Payload.hash == hash).
one()
static void * communicate(void *obj)
def _getCMSFrontierConnectionString
static std::string join(char **cmd)
def _getCMSOracleSQLAlchemyConnectionString
T get(const Candidate &c)
def _getCMSFrontierSQLAlchemyConnectionString
double scalar(const CLHEP::HepGenMatrix &m)
Return the matrix as a scalar. Raise an assertion if the matris is not .