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
20 logger = logging.getLogger(__name__)
24 if logger.level == logging.NOTSET:
25 logger.setLevel(logging.WARN)
30 cls._members = sorted([member
for member
in dir(cls)
if not member.startswith(
'_')])
31 cls._map =
dict([(member, getattr(cls, member))
for member
in cls._members])
32 cls._reversemap =
dict([(value, key)
for (key, value)
in cls._map.items()])
33 super(EnumMetaclass, cls).
__init__(name, bases, dct)
36 return len(cls._members)
39 '''Returns the value for this key (if the key is an integer,
40 the value is the nth member from the sorted members list).
43 if isinstance(key, int):
45 key = cls._members[key]
49 '''Returns the key for this value.
52 return cls._reversemap[value]
56 '''A la PEP 435, simplified.
59 __metaclass__ = EnumMetaclass
64 return hashlib.sha1(data).hexdigest()
71 description_length = 4000
72 hash_length = len(
hash(
''))
74 web_experts_email =
'cms-cond-dev@cern.ch'
75 offline_db_experts_email =
'cms-offlinedb-exp@cern.ch'
76 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'
78 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)
80 The database parameter (--db) refers to the database where the tool
81 will connect to read all the data. By default, the production account
82 (through Frontier) will be used.
84 In subcommands which take a source and a destination, --db always refers to
85 the source, and --destdb to the destination. For both of them the following
88 The database parameter can be an official alias, a filename or any
91 The official aliases are the following strings (first column):
93 Alias Level Database RO/RW Notes
94 ------------ ----------- ------------- ---------- -------------------------------
96 pro Production Frontier (ADG) read-only Default.
97 arc Archive Frontier read-only
98 int Integration Frontier read-only
99 dev Development Frontier read-only
100 boost Production Frontier read-only
101 boostprep Development Frontier read-only
103 orapro Production Oracle (ADG) read-only Password required.
104 oraarc Archive Oracle read-only Password required.
105 oraint Integration Oracle read-write Password required.
106 oradev Development Oracle read-write Password required.
107 oraboost Production Oracle (ADG) read-write Password required.
108 oraboostprep Development Oracle read-write Password required.
110 onlineorapro Production Oracle read-write Password required. Online only.
111 onlineoraint Online Int Oracle read-write Password required. Online only.
113 Most of the time, if you are a regular user, you will want to read/copy
114 conditions from the Frontier production account. Therefore, you can omit
115 the --db parameter, unless you want to read from somewhere else,
116 e.g. from your local SQLite file.
118 In addition, the parameter may be a filename (path) pointing to a local
122 relative/path/to/file.db
123 /absolute/path/to/file.db
125 Finally, any valid SQLAlchemy URL can be used. This allows full
126 flexibility in cases where it may be needed, e.g.
128 sqlite:// In-memory, volatile SQLite DB.
129 oracle://user@devdb11 Your private Oracle DB in devdb11 [*]
131 [*] See https://account.cern.ch/ -> Services for more information
132 on personal Oracle accounts.
134 For the official aliases, the password will be asked automatically
135 interactively. The same applies for Oracle URLs where the password
136 was not provided inside it, e.g.:
138 oracle://user@devdb11 The tool will prompt you for the password.
139 oracle://user:pass@devdb11 Password inlined. [+]
141 [+] Caution: Never write passwords in command-line parameters in
142 multi-user machines (e.g. lxplus), since other users can see them
143 in the process table (e.g. ps).
145 This means that both the official aliases and the filenames are shortcuts
146 to the full SQLAlchemy URL equivalents, e.g. the following are the same:
148 relative/path/to/file.db === sqlite:///relative/path/to/file.db
149 /absolute/path/to/file.db === sqlite:////absolute/path/to/file.db
154 validation =
'validation'
171 _Base = sqlalchemy.ext.declarative.declarative_base()
175 __tablename__ =
'TAG'
177 name = sqlalchemy.Column(sqlalchemy.String(name_length), primary_key=
True)
178 time_type = sqlalchemy.Column(sqlalchemy.Enum(*tuple(TimeType)), nullable=
False)
179 object_type = sqlalchemy.Column(sqlalchemy.String(name_length), nullable=
False)
180 synchronization = sqlalchemy.Column(sqlalchemy.Enum(*tuple(Synchronization)), nullable=
False)
181 description = sqlalchemy.Column(sqlalchemy.String(description_length), nullable=
False)
182 last_validated_time = sqlalchemy.Column(sqlalchemy.BIGINT, nullable=
False)
183 end_of_validity = sqlalchemy.Column(sqlalchemy.BIGINT, nullable=
False)
184 insertion_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=
False)
185 modification_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=
False)
187 iovs = sqlalchemy.orm.relationship(
'IOV')
191 __tablename__ =
'IOV'
193 tag_name = sqlalchemy.Column(sqlalchemy.ForeignKey(
'TAG.name'), primary_key=
True)
194 since = sqlalchemy.Column(sqlalchemy.BIGINT, primary_key=
True)
195 insertion_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, primary_key=
True)
196 payload_hash = sqlalchemy.Column(sqlalchemy.ForeignKey(
'PAYLOAD.hash'), nullable=
False)
198 tag = sqlalchemy.orm.relationship(
'Tag')
199 payload = sqlalchemy.orm.relationship(
'Payload')
203 __tablename__ =
'PAYLOAD'
205 hash = sqlalchemy.Column(sqlalchemy.CHAR(hash_length), primary_key=
True)
206 object_type = sqlalchemy.Column(sqlalchemy.String(name_length), nullable=
False)
207 data = sqlalchemy.Column(sqlalchemy.BLOB, nullable=
False)
208 streamer_info = sqlalchemy.Column(sqlalchemy.BLOB, nullable=
False)
209 version = sqlalchemy.Column(sqlalchemy.String(20), nullable=
False)
210 insertion_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=
False)
214 __tablename__ =
'GLOBAL_TAG'
216 name = sqlalchemy.Column(sqlalchemy.String(name_length), primary_key=
True)
217 validity = sqlalchemy.Column(sqlalchemy.BIGINT, nullable=
False)
218 description = sqlalchemy.Column(sqlalchemy.String(description_length), nullable=
False)
219 release = sqlalchemy.Column(sqlalchemy.String(name_length), nullable=
False)
220 insertion_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=
False)
221 snapshot_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=
False)
225 __tablename__ =
'GLOBAL_TAG_MAP'
227 global_tag_name = sqlalchemy.Column(sqlalchemy.ForeignKey(
'GLOBAL_TAG.name'), primary_key=
True)
228 record = sqlalchemy.Column(sqlalchemy.String(name_length), primary_key=
True)
229 label = sqlalchemy.Column(sqlalchemy.String(name_length), primary_key=
True)
230 tag_name = sqlalchemy.Column(sqlalchemy.ForeignKey(
'TAG.name'), nullable=
False)
232 global_tag = sqlalchemy.orm.relationship(
'GlobalTag')
233 tag = sqlalchemy.orm.relationship(
'Tag')
244 if url.drivername ==
'sqlite':
246 if not init
and url.database
is not None and not os.path.isfile(url.database):
248 raise Exception(
'SQLite database %s not found.' % url.database)
250 self.
engine = sqlalchemy.create_engine(url)
252 enabled_foreign_keys = self.engine.execute(
'pragma foreign_keys').
scalar()
253 supports_foreign_keys = enabled_foreign_keys
is not None
254 if not supports_foreign_keys:
255 logger.warning(
'Your SQLite database does not support foreign keys, so constraints will not be checked. Please upgrade.')
256 elif not enabled_foreign_keys:
257 self.engine.execute(
'pragma foreign_keys = on')
260 self.
engine = sqlalchemy.create_engine(url)
262 self.
_session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker(bind=self.
engine))
287 return _Base.metadata
310 '''Tests whether the current DB looks like a valid CMS Conditions one.
312 engine_connection = self.engine.connect()
315 ret =
all([self.engine.dialect.has_table(engine_connection, table.__tablename__)
for table
in [Tag, IOV, Payload]])
316 engine_connection.close()
320 '''Initializes a database.
324 logger.debug(
'Dropping tables...')
325 self.metadata.drop_all(self.
engine)
328 raise Exception(
'Looks like the database is already a valid CMS Conditions one. Please use drop=True if you really want to scratch it.')
330 logger.debug(
'Creating tables...')
331 self.metadata.create_all(self.
engine)
340 return subprocess.Popen([
'cmsGetFnConnect',
'frontier://%s' % database], stdout = subprocess.PIPE).
communicate()[0].strip()
349 return 'oracle://%s@%s' % (schema, database)
356 schema =
'cms_conditions'
357 if ':' in database
and '://' not in database:
358 database, schema = database.split(
':')
359 logging.debug(
' ... using db "%s", schema "%s"' % (database, schema) )
377 if database
in mapping:
378 database = mapping[database]()
380 logging.debug(
'connection string set to "%s"' % database)
383 url = sqlalchemy.engine.url.make_url(database)
384 except sqlalchemy.exc.ArgumentError:
385 url = sqlalchemy.engine.url.make_url(
'sqlite:///%s' % database)
389 '''Returns a Connection instance to the CMS Condition DB.
391 See database_help for the description of the database parameter.
393 The verbosity level is as follows:
395 0 = No output (default).
396 1 = SQL statements issued, including their parameters.
397 2 = In addition, results of the queries (all rows and the column headers).
400 if url.drivername ==
'oracle' and url.password
is None:
402 url.password = getpass.getpass(
'Password for %s: ' % str(url))
405 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.INFO)
408 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.DEBUG)
416 ret = session.query(primary_key).\
417 filter(primary_key == value).\
419 except sqlalchemy.exc.OperationalError:
425 '''To be used inside filter().
428 if timestamp
is None:
433 return sqlalchemy.literal(
True) == sqlalchemy.literal(
True)
435 return conddb.IOV.insertion_time <= _parse_timestamp(timestamp)
439 is_tag =
_exists(session, Tag.name, name)
442 result[
'type'] =
'Tag'
443 result[
'name'] = session.query(Tag).
get(name).name
444 result[
'timeType'] = session.query(Tag.time_type).\
445 filter(Tag.name == name).\
448 result[
'iovs'] = session.query(IOV.since, IOV.insertion_time, IOV.payload_hash, Payload.object_type).\
451 IOV.tag_name == name,
454 order_by(IOV.since.desc(), IOV.insertion_time.desc()).\
456 order_by(IOV.since, IOV.insertion_time).\
460 is_global_tag =
_exists(session, GlobalTag.name, name)
462 result[
'type'] =
'GlobalTag'
463 result[
'name'] = session.query(GlobalTag).
get(name)
464 result[
'tags'] = session.query(GlobalTagMap.record, GlobalTagMap.label, GlobalTagMap.tag_name).\
465 filter(GlobalTagMap.global_tag_name == name).\
466 order_by(GlobalTagMap.record, GlobalTagMap.label).\
468 except sqlalchemy.exc.OperationalError:
469 sys.stderr.write(
"No table for GlobalTags found in DB.\n\n")
471 if not is_tag
and not is_global_tag:
472 raise Exception(
'There is no tag or global tag named %s in the database.' % name)
478 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 .