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__)
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)
196 fqn =
'%s.%s' %(table, column)
197 if schema
is not None:
198 fqn =
'%s.%s' %(schema,fqn)
204 dbtype_name =
'%s_%s' %(baseType.__name__,backendName)
205 members[
'__tablename__'] = baseType.__tablename__
206 members[
'__table_args__'] =
None
207 if schemaName
is not None:
208 members[
'__table_args__'] = {
'schema': schemaName }
209 for k,v
in baseType.columns.items():
210 if isinstance(v[0],DbRef):
211 refColDbt = v[0].rtype.columns[v[0].rcol][0]
212 pk = (
True if v[1]==_Col.pk
else False)
214 members[k] = sqlalchemy.Column(refColDbt,sqlalchemy.ForeignKey(
fq_col(schemaName,v[0].rtype.__tablename__,v[0].rcol)),primary_key=
True)
216 nullable = (
False if v[1] == _Col.notNull
else True)
217 members[k] = sqlalchemy.Column(refColDbt,sqlalchemy.ForeignKey(
fq_col(schemaName,v[0].rtype.__tablename__,v[0].rcol)),nullable=nullable)
218 if v[0].rtype.__name__
not in deps_reg:
219 deps_reg.add(v[0].rtype.__name__)
220 reftype_name =
'%s_%s' %(v[0].rtype.__name__,backendName)
221 members[(v[0].rtype.__name__).lower()] = sqlalchemy.orm.relationship(reftype_name)
224 members[k] = sqlalchemy.Column(v[0],primary_key=
True)
226 nullable = (
True if v[1]==_Col.nullable
else False)
227 members[k] = sqlalchemy.Column(v[0],nullable=nullable)
228 dbType = type(dbtype_name,(_Base,),members)
230 if backendName
not in db_models.keys():
231 db_models[backendName] = {}
232 db_models[backendName][baseType.__name__] = dbType
236 if tp.__table_args__
is not None:
237 return tp.__table_args__[
'schema']
241 __tablename__ =
'TAG'
242 columns = {
'name': (sqlalchemy.String(name_length),_Col.pk),
243 'time_type': (sqlalchemy.Enum(*tuple(TimeType)),_Col.notNull),
244 'object_type': (sqlalchemy.String(name_length),_Col.notNull),
245 'synchronization': (sqlalchemy.Enum(*tuple(Synchronization)),_Col.notNull),
246 'description': (sqlalchemy.String(description_length),_Col.notNull),
247 'last_validated_time':(sqlalchemy.BIGINT,_Col.notNull),
248 'end_of_validity':(sqlalchemy.BIGINT,_Col.notNull),
249 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
250 'modification_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
254 __tablename__ =
'PAYLOAD'
255 columns = {
'hash': (sqlalchemy.CHAR(hash_length),_Col.pk),
256 'object_type': (sqlalchemy.String(name_length),_Col.notNull),
257 'data': (sqlalchemy.BLOB,_Col.notNull),
258 'streamer_info':(sqlalchemy.BLOB,_Col.notNull),
259 'version':(sqlalchemy.String(20),_Col.notNull),
260 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
264 __tablename__ =
'IOV'
265 columns = {
'tag_name':(
DbRef(Tag,
'name'),_Col.pk),
266 'since':(sqlalchemy.BIGINT,_Col.pk),
267 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.pk),
268 'payload_hash':(
DbRef(Payload,
'hash'),_Col.pk) }
272 __tablename__ =
'GLOBAL_TAG'
273 columns = {
'name':(sqlalchemy.String(name_length),_Col.pk),
274 'validity': (sqlalchemy.BIGINT,_Col.notNull),
275 'description':(sqlalchemy.String(description_length),_Col.notNull),
276 'release':(sqlalchemy.String(name_length),_Col.notNull),
277 'insertion_time':(sqlalchemy.TIMESTAMP,_Col.notNull),
278 'snapshot_time':(sqlalchemy.TIMESTAMP,_Col.notNull) }
281 __tablename__ =
'GLOBAL_TAG_MAP'
282 columns = {
'global_tag_name':(
DbRef(GlobalTag,
'name'),_Col.pk),
283 'record':(sqlalchemy.String(name_length),_Col.pk),
284 'label':(sqlalchemy.String(name_length),_Col.pk),
285 'tag_name':(
DbRef(Tag,
'name'),_Col.notNull) }
290 __tablename__ =
'TAG_LOG'
291 columns = {
'tag_name':(
DbRef(Tag,
'name'),_Col.pk),
292 'event_time':(sqlalchemy.TIMESTAMP,_Col.pk),
293 'action':(sqlalchemy.String(100),_Col.pk),
294 'user_name':(sqlalchemy.String(100),_Col.notNull),
295 'host_name':(sqlalchemy.String(100),_Col.notNull),
296 'command':(sqlalchemy.String(500),_Col.notNull),
297 'user_text':(sqlalchemy.String(4000),_Col.notNull) }
308 if url.drivername ==
'sqlite':
314 self.
engine = sqlalchemy.create_engine(url)
316 enabled_foreign_keys = self.engine.execute(
'pragma foreign_keys').
scalar()
317 supports_foreign_keys = enabled_foreign_keys
is not None
318 if not supports_foreign_keys:
319 logger.warning(
'Your SQLite database does not support foreign keys, so constraints will not be checked. Please upgrade.')
320 elif not enabled_foreign_keys:
321 self.engine.execute(
'pragma foreign_keys = on')
324 self.
engine = sqlalchemy.create_engine(url)
326 self.
_session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker(bind=self.
engine))
348 logging.debug(
' ... using db "%s", schema "%s"' % (url, self.
_schemaName) )
349 logging.debug(
'Loading db types...')
359 basename = theType.__name__
373 return _Base.metadata
396 '''Tests whether the current DB looks like a valid CMS Conditions one.
398 engine_connection = self.engine.connect()
403 ret =
all([self.engine.dialect.has_table(engine_connection, table.__tablename__,
getSchema(table))
for table
in [_Tag, _IOV, _Payload]])
404 engine_connection.close()
408 '''Initializes a database.
410 logging.info(
'Initializing database...')
412 logging.debug(
'Dropping tables...')
413 self.metadata.drop_all(self.
engine)
416 raise Exception(
'Looks like the database is already a valid CMS Conditions one.')
418 logging.debug(
'Creating tables...')
430 return subprocess.Popen([
'cmsGetFnConnect',
'frontier://%s' % database], stdout = subprocess.PIPE).
communicate()[0].strip()
433 if technology ==
'frontier':
436 elif technology ==
'oracle':
437 return '%s://%s@%s' % (technology, schema_name, service)
442 if ':' in database
and '://' not in database:
443 database, schema = database.split(
':')
447 'pro' : (
'frontier',
'PromptProd', {
'R': schema_name }, ),
448 'arc' : (
'frontier',
'FrontierArc', {
'R': schema_name }, ),
449 'int' : (
'frontier',
'FrontierInt', {
'R': schema_name }, ),
450 'dev' : (
'frontier',
'FrontierPrep', {
'R': schema_name }, ),
452 'orapro': (
'oracle',
'cms_orcon_adg', {
'R': dbreader_user_name }, ),
453 'oraarc': (
'oracle',
'cmsarc_lb', {
'R': dbreader_user_name }, ),
455 'oraint': (
'oracle',
'cms_orcoff_int', {
'R': dbreader_user_name,
456 'W': dbwriter_user_name }, ),
457 'oradev': (
'oracle',
'cms_orcoff_prep', {
'R': dbreader_user_name,
458 'W': devdbwriter_user_name }, ),
459 'onlineorapro': (
'oracle',
'cms_orcon_prod', {
'R': dbreader_user_name,
460 'W': dbwriter_user_name }, ),
461 'onlineoraint': (
'oracle',
'cmsintr_lb', {
'R': dbreader_user_name,
462 'W': dbwriter_user_name }, ),
465 if database
in officialdbs.keys():
466 key = (
'R' if read_only else 'W')
467 mapping = officialdbs[database]
470 schema_dict = mapping[2]
471 if key
in schema_dict.keys():
474 raise Exception(
"Read-only database %s://%s cannot be accessed in update mode." %(tech,service))
476 logging.debug(
'connection string set to "%s"' % database)
479 url = sqlalchemy.engine.url.make_url(database)
480 except sqlalchemy.exc.ArgumentError:
481 url = sqlalchemy.engine.url.make_url(
'sqlite:///%s' % database)
484 def connect(url, init=False, authPath=None, verbose=0):
485 '''Returns a Connection instance to the CMS Condition DB.
487 See database_help for the description of the database parameter.
489 The verbosity level is as follows:
491 0 = No output (default).
492 1 = SQL statements issued, including their parameters.
493 2 = In addition, results of the queries (all rows and the column headers).
496 if url.drivername ==
'oracle':
497 if url.username
is None:
498 logging.error(
'Could not resolve the username for the connection %s. Please provide a connection in the format oracle://[user]:[pass]@[host]' %url )
499 raise Exception(
'Connection format error: %s' %url )
500 if url.password
is None:
502 if authPathEnvVar
in os.environ:
503 authPath = os.environ[authPathEnvVar]
505 if authPath
is not None:
506 authFile = os.path.join(authPath,
'.netrc')
507 if authFile
is not None:
508 print 'url=%s host=%s username=%s' %(url,url.host,url.username)
509 entryKey = url.host.lower()+
"/"+url.username.lower()
510 logging.debug(
'Looking up credentials for %s in file %s ' %(entryKey,authFile) )
512 params = netrc.netrc( authFile ).authenticators(entryKey)
513 if params
is not None:
514 (username, account, password) = params
515 url.password = password
517 msg =
'The entry %s has not been found in the .netrc file.' %entryKey
521 pwd = getpass.getpass(
'Password for %s: ' % str(url))
522 if pwd
is None or pwd ==
'':
523 pwd = getpass.getpass(
'Password for %s: ' % str(url))
524 if pwd
is None or pwd ==
'':
525 raise Exception(
'Empty password provided, bailing out...')
529 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.INFO)
532 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.DEBUG)
540 ret = session.query(primary_key).\
541 filter(primary_key == value).\
543 except sqlalchemy.exc.OperationalError:
549 '''To be used inside filter().
552 if timestamp
is None:
557 return sqlalchemy.literal(
True) == sqlalchemy.literal(
True)
559 return conddb.IOV.insertion_time <= _parse_timestamp(timestamp)
563 is_tag =
_exists(session, Tag.name, name)
566 result[
'type'] =
'Tag'
567 result[
'name'] = session.query(Tag).
get(name).name
568 result[
'timeType'] = session.query(Tag.time_type).\
569 filter(Tag.name == name).\
572 result[
'iovs'] = session.query(IOV.since, IOV.insertion_time, IOV.payload_hash, Payload.object_type).\
575 IOV.tag_name == name,
578 order_by(IOV.since.desc(), IOV.insertion_time.desc()).\
580 order_by(IOV.since, IOV.insertion_time).\
584 is_global_tag =
_exists(session, GlobalTag.name, name)
586 result[
'type'] =
'GlobalTag'
587 result[
'name'] = session.query(GlobalTag).
get(name)
588 result[
'tags'] = session.query(GlobalTagMap.record, GlobalTagMap.label, GlobalTagMap.tag_name).\
589 filter(GlobalTagMap.global_tag_name == name).\
590 order_by(GlobalTagMap.record, GlobalTagMap.label).\
592 except sqlalchemy.exc.OperationalError:
593 sys.stderr.write(
"No table for GlobalTags found in DB.\n\n")
595 if not is_tag
and not is_global_tag:
596 raise Exception(
'There is no tag or global tag named %s in the database.' % name)
602 data, payloadType = session.query(Payload.data, Payload.object_type).
filter(Payload.hash == hash).
one()
def _getCMSSQLAlchemyConnectionString
static void * communicate(void *obj)
def _getCMSFrontierConnectionString
static std::string join(char **cmd)
T get(const Candidate &c)
double scalar(const CLHEP::HepGenMatrix &m)
Return the matrix as a scalar. Raise an assertion if the matris is not .