CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
querying.py
Go to the documentation of this file.
1 """
2 
3 Translates a given database name alias, and credentials taken from a file, into an oracle database string, then connects.
4 Also sets up ORM with SQLAlchemy.
5 
6 """
7 
8 import sqlalchemy
9 from sqlalchemy import create_engine, text, or_
10 from sqlalchemy.orm import sessionmaker
11 import datetime
12 from data_sources import json_data_node
13 from copy import deepcopy
14 
15 class connection():
16  row_limit = 1000
17  engine = None
18  connection = None
19  session = None
20  connection_data = None
21  base = None
22  netrc_authenticators = None
23  secrets = None
24  # init creates a dictionary of secrets found in the secrets file
25  # next stage of this will be to search different directories (assigning priority to directories)
26  # looking for an appropriate netrc file - if none is found, ask for password
27  def __init__(self, connection_data):
28  # is not needed in cmssw
29  """try:
30  import cx_Oracle
31  except ImportError as e:
32  exit("cx_Oracle cannot be imported - try to run 'source /data/cmssw/setupEnv.sh' and 'source venv/bin/activate'.")"""
33 
34  # todo translation on connection_data - it may be a string
35  # find out which formats of db string are acceptable
36  frontier_str_length = len("frontier://")
37  sqlite_str_length = len("sqlite:///")
38  if type(connection_data) == str and connection_data[0:frontier_str_length] == "frontier://":
39  db_name = connection_data[frontier_str_length:].split("/")[0]
40  schema = connection_data[frontier_str_length:].split("/")[1]
41  connection_data = {}
42  connection_data["db_alias"] = db_name
43  connection_data["schema"] = schema
44  connection_data["host"] = "frontier"
45  """elif type(connection_data) == str and connection_data[0:sqlite_str_length] == "sqlite:///":
46  db_name = connection_data[frontier_str_length:]
47  schema = ""
48  connection_data = {}
49  connection_data["db_alias"] = db_name
50  connection_data["schema"] = schema
51  connection_data["host"] = "sqlite"
52  """
53 
54  headers = ["login", "account", "password"]
55  self.connection_data = connection_data
56 
57  try:
58  self.schema = connection_data["schema"]
59  except KeyError as k:
60  self.schema = ""
61 
62  # setup authentication
63  import netrc
64  if connection_data["host"] == "oracle":
65  self.secrets = dict(zip(headers, netrc.netrc(connection_data["secrets"]).authenticators(connection_data["host"])))
66  self.netrc_authenticators = netrc.netrc(connection_data["secrets"])
67 
68  import models as ms
69  self.models = ms.generate()
70  self.base = self.models["Base"]
71 
72  # setup engine with given credentials from netrc file, and make a session maker
73  def setup(self):
74 
75  self.db_name = self.connection_data["db_alias"]
76 
77  if self.connection_data["host"] != "sqlite":
78  if self.connection_data["host"] != "frontier":
79  # if not frontier, we have to authenticate
80  user = self.secrets["login"]
81  pwd = self.secrets["password"]
82  self.engine = create_engine(self.build_oracle_url(user, pwd, self.db_name))
83  else:
84  # if frontier, no need to authenticate
85  self.engine = create_engine(self.build_frontier_url(self.db_name, self.schema))
86  else:
87  # if host is sqlite, making the url is easy - no authentication
88  self.engine = create_engine("sqlite:///%s" % self.db_name)
89 
90  self.sessionmaker = sessionmaker(bind=self.engine)
91  self.session = self.sessionmaker()
92  self.factory = factory(self)
93 
94  # assign correct schema for database name to each model
95  tmp_models_dict = {}
96  for key in self.models:
97  try:
98  if self.models[key].__class__ == sqlalchemy.ext.declarative.api.DeclarativeMeta\
99  and str(self.models[key].__name__) != "Base":
100 
101  self.models[key].__table__.schema = self.schema
102 
103  self.models[key].session = self.session
104  self.models[key].authentication = self.netrc_authenticators
105  self.models[key].secrets = self.secrets
106  tmp_models_dict[key.lower()] = self.models[key]
107  tmp_models_dict[key.lower()].empty = False
108  except AttributeError:
109  continue
110 
111  self.models = tmp_models_dict
112 
113  return self
114 
115  def deepcopy_model(self, model):
116  new_dict = dict(model.__dict__)
117  new_dict["__table__"] = deepcopy(model.__dict__["__table__"])
118  return type(model.__class__.__name__, (), new_dict)
119 
120  def close_session(self):
121  try:
122  self.session.close()
123  return True
124  except Exception as e:
125  exit(e)
126 
128  try:
129  import subprocess
130  return subprocess.Popen(['cmsGetFnConnect', 'frontier://%s' % database], stdout = subprocess.PIPE).communicate()[0].strip()
131  except:
132  exit("Frontier connections can only be constructed when inside a CMSSW environment.")
133 
134  # get database string for frontier
135  def _cms_frontier_string(self, database, schema="cms_conditions"):
136  import urllib
137  return 'oracle+frontier://@%s/%s' % (urllib.quote_plus(self._get_CMS_frontier_connection_string(database)), schema)
138 
139  # get database string for oracle
140  def _cms_oracle_string(self, user, pwd, db_name):
141  return 'oracle://%s:%s@%s' % (user, pwd, db_name)
142 
143  # build the connection url, and get credentials from self.secrets dictionary
144  def build_oracle_url(self, user, pwd, db_name):
145  # map db_name to the connection url
146  # pretty much the same as in conddblib.py in cmssw
147  mapping = {
148  'orapro': (lambda: self._cms_oracle_string(user, pwd, 'cms_orcon_adg')),
149  'oraarc': (lambda: self._cms_oracle_string(user, pwd, 'cmsarc_lb')),
150  'oraint': (lambda: self._cms_oracle_string(user, pwd, 'cms_orcoff_int')),
151  'oradev': (lambda: self._cms_oracle_string('cms_conditions_002', pwd, 'cms_orcoff_prep')),
152  'oraboost': (lambda: self._cms_oracle_string('cms_conditions', pwd, 'cms_orcon_adg')),
153  'oraboostprep': (lambda: self._cms_oracle_string('cms_conditions_002', pwd, 'cms_orcoff_prep')),
154 
155  'onlineorapro': (lambda: self._cms_oracle_string(user, pwd, 'cms_orcon_prod')),
156  'onlineoraint': (lambda: self._cms_oracle_string(user, pwd, 'cmsintr_lb')),
157  }
158 
159  if db_name in mapping.keys():
160  database_url = mapping[db_name]()
161  else:
162  print("Database name given isn't valid.")
163  return
164 
165  try:
166  url = sqlalchemy.engine.url.make_url(database_url)
167  if url.password is None:
168  url.password = pwd
169  except sqlalchemy.exc.ArgumentError:
170  url = sqlalchemy.engine.url.make_url('sqlite:///%s' % db_name)
171  return url
172 
173  def build_frontier_url(self, db_name, schema):
174 
175  mapping = {
176  'pro': lambda: self._cms_frontier_string('PromptProd', schema),
177  'arc': lambda: self._cms_frontier_string('FrontierArc', schema),
178  'int': lambda: self._cms_frontier_string('FrontierInt', schema),
179  'dev': lambda: self._cms_frontier_string('FrontierPrep', schema)
180  }
181 
182  if db_name in mapping.keys():
183  database_url = mapping[db_name]()
184  else:
185  print("Database name given isn't valid.")
186  return
187 
188  try:
189  url = sqlalchemy.engine.url.make_url(database_url)
190  except sqlalchemy.exc.ArgumentError:
191  url = sqlalchemy.engine.url.make_url('sqlite:///%s' % db_name)
192  return url
193 
194  def __repr__(self):
195  return "<connection db='%s'>" % self.db_name
196 
197  @staticmethod
199  class_name = cls.__name__
200  all_upper_case = True
201  for character in class_name:
202  all_upper_case = character.isupper()
203  if all_upper_case:
204  return class_name
205  for n in range(0, len(class_name)):
206  if class_name[n].isupper() and n != 0:
207  class_name = str(class_name[0:n]) + "".join(["_", class_name[n].lower()]) + str(class_name[n+1:])
208  elif class_name[n].isupper() and n == 0:
209  class_name = str(class_name[0:n]) + "".join([class_name[n].lower()]) + str(class_name[n+1:])
210  return class_name
211 
212  # get model based on given model name
213  def model(self, model_name):
214  if model_name.__class__ == sqlalchemy.ext.declarative.api.DeclarativeMeta:
215  model_name = model_name.__name__
216  model_name = model_name.replace("_", "")
217  return self.models[model_name]
218 
219  # model should be the class the developer wants to be instantiated
220  # pk_to_value maps primary keys to values
221  # if the result returned from the query is not unique, no object is created
222  def object(self, model, pk_to_value):
223  if self.session == None:
224  return None
225  model_data = self.session.query(model)
226  for pk in pk_to_value:
227  model_data = model_data.filter(model.__dict__[pk] == pk_to_value[pk])
228  return model_data.first()
229 
230  def global_tag(self, **pkargs):
231  return self.factory.object("globaltag", **pkargs)
232 
233  def global_tag_map(self, **pkargs):
234  return self.factory.object("globaltagmap", **pkargs)
235 
236  def global_tag_map_request(self, **pkargs):
237  return self.factory.object("globaltagmaprequest", **pkargs)
238 
239  def tag(self, **pkargs):
240  return self.factory.object("tag", **pkargs)
241 
242  def iov(self, **pkargs):
243  return self.factory.object("iov", **pkargs)
244 
245  def payload(self, **pkargs):
246  return self.factory.object("payload", **pkargs)
247 
248  def record(self, **pkargs):
249  return self.factory.object("payload", **pkargs)
250 
251  # adds %% at the beginning and end so LIKE in SQL searches all of the string
252  def _oracle_match_format(self, string):
253  return "%%%s%%" % string
254 
255  # returns dictionary mapping object type to a list of all objects found in the search
256  def search_everything(self, string, amount=10):
257  string = self._oracle_match_format(string)
258 
259  gt = self.model("globaltag")
260  global_tags = self.session.query(gt).filter(or_(
261  gt.name.ilike(string),
262  gt.description.ilike(string),
263  gt.release.ilike(string)
264  )).limit(amount)
265  tag = self.model("tag")
266  tags = self.session.query(tag).filter(or_(
267  tag.name.ilike(string),
268  tag.object_type.ilike(string),
269  tag.description.ilike(string))
270  ).limit(amount)
271  iov = self.model("iov")
272  iovs = self.session.query(iov).filter(or_(
273  iov.tag_name.ilike(string),
274  iov.since.ilike(string),
275  iov.payload_hash.ilike(string),
276  iov.insertion_time.ilike(string)
277  )).limit(amount)
278  payload = self.model("payload")
279  payloads = self.session.query(payload).filter(or_(
280  payload.hash.ilike(string),
281  payload.object_type.ilike(string),
282  payload.insertion_time.ilike(string)
283  )).limit(amount)
284 
285  return json_data_node.make({
286  "global_tags" : global_tags.all(),
287  "tags" : tags.all(),
288  "iovs" : iovs.all(),
289  "payloads" : payloads.all()
290  })
291 
292  # if on sqlite
293 
294  def write(self, object):
295  if self.connection_data["host"] == "sqlite":
296  if self.session != None:
297  class_of_object = object.__class__
298  new_object = class_of_object(object.as_dicts(), convert_timestamps=False)
299  new_object.__table__.schema = self.schema
300  self.session.add(new_object)
301  return new_object
302  else:
303  print("Writing to non-sqlite databases currently not supported.")
304 
305  def commit(self):
306  if self.connection_data["host"] == "sqlite":
307  if self.session != None:
308  self.session.commit()
309  else:
310  print("Writing to non-sqlite databases currently not supported.")
311 
312  def write_and_commit(self, object):
313  # should be changed to deal with errors - add them to exception handling if they appear
314  self.write(object)
315  self.commit()
316 
317 
318 # contains methods for creating objects
319 class factory():
320 
321  def __init__(self, connection):
322  self.connection = connection
323 
324  # class_name is the class name of the model to be used
325  # pkargs is a dictionary of keyword arguments used as primary key values
326  # this dictionary will be used to populate the object of type name class_name
327  def object(self, class_name, **pkargs):
328  from data_sources import json_list
329  model = self.connection.model(class_name)
330  if self.connection.session == None:
331  return None
332  model_data = self.connection.session.query(model)
333  if len(pkargs.items()) != 0:
334  for pk in pkargs:
335  if pkargs[pk].__class__ != list:
336  if pkargs[pk].__class__ == json_list:
337  pkargs[pk] = pkargs[pk].data()
338  else:
339  pkargs[pk] = [pkargs[pk]]
340  model_data = model_data.filter(model.__dict__[pk].in_(pkargs[pk]))
341  if model_data.count() > 1:
342  return json_list(model_data.all())
343  elif model_data.count() == 1:
344  return model_data.first()
345  else:
346  return None
347  else:
348  new_object = model()
349  new_object.empty = True
350  return new_object
351 
352 def connect(connection_data):
353  con = connection(connection_data=connection_data)
354  con = con.setup()
355  return con
def global_tag_map_request
Definition: querying.py:236
std::string print(const Track &, edm::Verbosity=edm::Concise)
Track print utility.
Definition: print.cc:10
def _get_CMS_frontier_connection_string
Definition: querying.py:127
static void * communicate(void *obj)
Definition: DQMNet.cc:1246
def connect
Definition: querying.py:352
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
double split
Definition: MVATrainer.cc:139