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 Development Frontier read-only
102 orapro Production Oracle (ADG) read-only Password required.
103 oraarc Archive Oracle read-only Password required.
104 oraint Integration Oracle read-write Password required.
105 oradev Development Oracle read-write Password required.
106 oraboost Development Oracle read-write Password required.
108 onlineorapro Production Oracle read-write Password required. Online only.
109 onlineoraint Online Int Oracle read-write Password required. Online only.
111 Most of the time, if you are a regular user, you will want to read/copy
112 conditions from the Frontier production account. Therefore, you can omit
113 the --db parameter, unless you want to read from somewhere else,
114 e.g. from your local SQLite file.
116 In addition, the parameter may be a filename (path) pointing to a local
120 relative/path/to/file.db
121 /absolute/path/to/file.db
123 Finally, any valid SQLAlchemy URL can be used. This allows full
124 flexibility in cases where it may be needed, e.g.
126 sqlite:// In-memory, volatile SQLite DB.
127 oracle://user@devdb11 Your private Oracle DB in devdb11 [*]
129 [*] See https://account.cern.ch/ -> Services for more information
130 on personal Oracle accounts.
132 For the official aliases, the password will be asked automatically
133 interactively. The same applies for Oracle URLs where the password
134 was not provided inside it, e.g.:
136 oracle://user@devdb11 The tool will prompt you for the password.
137 oracle://user:pass@devdb11 Password inlined. [+]
139 [+] Caution: Never write passwords in command-line parameters in
140 multi-user machines (e.g. lxplus), since other users can see them
141 in the process table (e.g. ps).
143 This means that both the official aliases and the filenames are shortcuts
144 to the full SQLAlchemy URL equivalents, e.g. the following are the same:
146 relative/path/to/file.db === sqlite:///relative/path/to/file.db
147 /absolute/path/to/file.db === sqlite:////absolute/path/to/file.db
166 _Base = sqlalchemy.ext.declarative.declarative_base()
170 __tablename__ =
'tag'
172 name = sqlalchemy.Column(sqlalchemy.String(name_length), primary_key=
True)
173 time_type = sqlalchemy.Column(sqlalchemy.Enum(*tuple(TimeType)), nullable=
False)
174 object_type = sqlalchemy.Column(sqlalchemy.String(name_length), nullable=
False)
175 synchronization = sqlalchemy.Column(sqlalchemy.Enum(*tuple(Synchronization)), nullable=
False)
176 description = sqlalchemy.Column(sqlalchemy.String(description_length), nullable=
False)
177 last_validated_time = sqlalchemy.Column(sqlalchemy.Integer, nullable=
False)
178 end_of_validity = sqlalchemy.Column(sqlalchemy.Integer, nullable=
False)
179 insertion_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=
False)
180 modification_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=
False)
182 iovs = sqlalchemy.orm.relationship(
'IOV')
186 __tablename__ =
'iov'
188 tag_name = sqlalchemy.Column(sqlalchemy.ForeignKey(
'tag.name'), primary_key=
True)
189 since = sqlalchemy.Column(sqlalchemy.Integer, primary_key=
True)
190 insertion_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, primary_key=
True)
191 payload_hash = sqlalchemy.Column(sqlalchemy.ForeignKey(
'payload.hash'), nullable=
False)
193 tag = sqlalchemy.orm.relationship(
'Tag')
194 payload = sqlalchemy.orm.relationship(
'Payload')
198 __tablename__ =
'payload'
200 hash = sqlalchemy.Column(sqlalchemy.CHAR(hash_length), primary_key=
True)
201 object_type = sqlalchemy.Column(sqlalchemy.String(name_length), nullable=
False)
202 data = sqlalchemy.Column(sqlalchemy.BLOB, nullable=
False)
203 streamer_info = sqlalchemy.Column(sqlalchemy.BLOB, nullable=
False)
204 version = sqlalchemy.Column(sqlalchemy.String(20), nullable=
False)
205 insertion_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=
False)
209 __tablename__ =
'global_tag'
211 name = sqlalchemy.Column(sqlalchemy.String(name_length), primary_key=
True)
212 validity = sqlalchemy.Column(sqlalchemy.Integer, nullable=
False)
213 description = sqlalchemy.Column(sqlalchemy.String(description_length), nullable=
False)
214 release = sqlalchemy.Column(sqlalchemy.String(name_length), nullable=
False)
215 insertion_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=
False)
216 snapshot_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=
False)
220 __tablename__ =
'global_tag_map'
222 global_tag_name = sqlalchemy.Column(sqlalchemy.ForeignKey(
'global_tag.name'), primary_key=
True)
223 record = sqlalchemy.Column(sqlalchemy.String(name_length), primary_key=
True)
224 label = sqlalchemy.Column(sqlalchemy.String(name_length), primary_key=
True)
225 tag_name = sqlalchemy.Column(sqlalchemy.ForeignKey(
'tag.name'), nullable=
False)
227 global_tag = sqlalchemy.orm.relationship(
'GlobalTag')
228 tag = sqlalchemy.orm.relationship(
'Tag')
239 if url.drivername ==
'sqlite':
241 if not init
and url.database
is not None and not os.path.isfile(url.database):
243 raise Exception(
'SQLite database %s not found.' % url.database)
245 self.
engine = sqlalchemy.create_engine(url)
247 enabled_foreign_keys = self.engine.execute(
'pragma foreign_keys').
scalar()
248 supports_foreign_keys = enabled_foreign_keys
is not None
249 if not supports_foreign_keys:
250 logger.warning(
'Your SQLite database does not support foreign keys, so constraints will not be checked. Please upgrade.')
251 elif not enabled_foreign_keys:
252 self.engine.execute(
'pragma foreign_keys = on')
255 self.
engine = sqlalchemy.create_engine(url)
257 self.
_session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker(bind=self.
engine))
282 return _Base.metadata
305 '''Tests whether the current DB looks like a valid CMS Conditions one.
308 engine_connection = self.engine.connect()
309 ret =
all([self.engine.dialect.has_table(engine_connection, table.__tablename__)
for table
in [Tag, IOV, Payload, GlobalTag, GlobalTagMap]])
310 engine_connection.close()
314 '''Initializes a database.
318 logger.debug(
'Dropping tables...')
319 self.metadata.drop_all(self.
engine)
322 raise Exception(
'Looks like the database is already a valid CMS Conditions one. Please use drop=True if you really want to scratch it.')
324 logger.debug(
'Creating tables...')
325 self.metadata.create_all(self.
engine)
334 return subprocess.Popen([
'cmsGetFnConnect',
'frontier://%s' % database], stdout = subprocess.PIPE).
communicate()[0].strip()
343 return 'oracle://%s@%s' % (schema, database)
347 def connect(database='pro', init=False, verbose=0):
348 '''Returns a Connection instance to the CMS Condition DB.
350 See database_help for the description of the database parameter.
352 The verbosity level is as follows:
354 0 = No output (default).
355 1 = SQL statements issued, including their parameters.
356 2 = In addition, results of the queries (all rows and the column headers).
377 if database
in mapping:
378 database = mapping[database]()
381 url = sqlalchemy.engine.url.make_url(database)
382 if url.drivername ==
'oracle' and url.password
is None:
384 url.password = getpass.getpass(
'Password for %s: ' % str(url))
385 except sqlalchemy.exc.ArgumentError:
386 url = sqlalchemy.engine.url.make_url(
'sqlite:///%s' % database)
389 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.INFO)
392 logging.getLogger(
'sqlalchemy.engine').setLevel(logging.DEBUG)
static void * communicate(void *obj)
def _getCMSFrontierConnectionString
def _getCMSOracleSQLAlchemyConnectionString
def _getCMSFrontierSQLAlchemyConnectionString
double scalar(const CLHEP::HepGenMatrix &m)
Return the matrix as a scalar. Raise an assertion if the matris is not .