CMS 3D CMS Logo

o2olib.py
Go to the documentation of this file.
1 from __future__ import print_function
2 __author__ = 'Giacomo Govi'
3 
4 import sqlalchemy
5 import sqlalchemy.ext.declarative
6 import subprocess
7 from datetime import datetime
8 import os
9 import sys
10 import logging
11 import string
12 import json
13 
14 import CondCore.Utilities.credentials as auth
15 
16 prod_db_service = 'cms_orcon_prod'
17 dev_db_service = 'cms_orcoff_prep'
18 schema_name = 'CMS_CONDITIONS'
19 sqlalchemy_tpl = 'oracle://%s:%s@%s'
20 coral_tpl = 'oracle://%s/%s'
21 private_db = 'sqlite:///o2o_jobs.db'
22 startStatus = -1
23 messageLevelEnvVar = 'O2O_LOG_LEVEL'
24 logFolderEnvVar = 'O2O_LOG_FOLDER'
25 logger = logging.getLogger(__name__)
26 
27 _Base = sqlalchemy.ext.declarative.declarative_base()
28 
29 class O2OJob(_Base):
30  __tablename__ = 'O2O_JOB'
31  __table_args__ = {'schema' : schema_name}
32  name = sqlalchemy.Column(sqlalchemy.String(100), primary_key=True)
33  enabled = sqlalchemy.Column(sqlalchemy.Integer, nullable=False)
34  frequent = sqlalchemy.Column(sqlalchemy.Integer, nullable=False)
35  tag_name = sqlalchemy.Column(sqlalchemy.String(100), nullable=False)
36  interval = sqlalchemy.Column(sqlalchemy.Integer, nullable=False)
37 
38 class O2OJobConf(_Base):
39  __tablename__ = 'O2O_JOB_CONF'
40  __table_args__ = {'schema' : schema_name}
41  job_name = sqlalchemy.Column(sqlalchemy.ForeignKey(O2OJob.name), primary_key=True)
42  insertion_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, primary_key=True)
43  configuration = sqlalchemy.Column(sqlalchemy.String(4000), nullable=False)
44 
45  job = sqlalchemy.orm.relationship('O2OJob', primaryjoin="O2OJob.name==O2OJobConf.job_name")
46 
47 class O2ORun(_Base):
48  __tablename__ = 'O2O_RUN'
49  __table_args__ = {'schema' : schema_name}
50  job_name = sqlalchemy.Column(sqlalchemy.ForeignKey(O2OJob.name), primary_key=True)
51  start_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, primary_key=True)
52  end_time = sqlalchemy.Column(sqlalchemy.TIMESTAMP, nullable=True)
53  status_code = sqlalchemy.Column(sqlalchemy.Integer, nullable=False)
54  log = sqlalchemy.Column(sqlalchemy.CLOB, nullable=True)
55 
56  job = sqlalchemy.orm.relationship('O2OJob', primaryjoin="O2OJob.name==O2ORun.job_name")
57 
58 def print_table( headers, table ):
59  ws = []
60  for h in headers:
61  ws.append(len(h))
62  for row in table:
63  ind = 0
64  for c in row:
65  if ind<len(ws):
66  if len(c)> ws[ind]:
67  ws[ind] = len(c)
68  ind += 1
69 
70  def printf( row ):
71  line = ''
72  ind = 0
73  for w in ws:
74  fmt = '{:<%s}' %w
75  if ind<len(ws):
76  line += (fmt.format( row[ind] )+' ')
77  ind += 1
78  print(line)
79  printf( headers )
80  hsep = ''
81  for w in ws:
82  fmt = '{:-<%s}' %w
83  hsep += (fmt.format('')+' ')
84  print(hsep)
85  for row in table:
86  printf( row )
87 
88 
90 
91  def __init__( self , logLevel):
92  self.db_connection = None
93  self.conf_dict = {}
94  fmt_str = "[%(asctime)s] %(levelname)s: %(message)s"
95  if messageLevelEnvVar in os.environ:
96  levStr = os.environ[messageLevelEnvVar]
97  if levStr == 'DEBUG':
98  logLevel = logging.DEBUG
99  logFormatter = logging.Formatter(fmt_str)
100 
101  self.logger = logging.getLogger()
102  self.logger.setLevel(logLevel)
103  consoleHandler = logging.StreamHandler(sys.stdout)
104  consoleHandler.setFormatter(logFormatter)
105  self.logger.addHandler(consoleHandler)
106  self.eng = None
107 
108  def getSession( self, db_service, role, authPath ):
109  url = None
110  if db_service is None:
111  url = private_db
112  else:
113  self.logger.info('Getting credentials')
114  if authPath is not None:
115  if not os.path.exists(authPath):
116  self.logger.error('Authentication path %s is invalid.' %authPath)
117  return None
118  try:
119  (username, account, pwd) = auth.get_credentials_for_schema( db_service, schema_name, role, authPath )
120  except Exception as e:
121  self.logger.debug(str(e))
122  username = None
123  pwd = None
124  if username is None:
125  self.logger.error('Credentials for service %s are not available' %db_service)
126  raise Exception("Cannot connect to db %s" %db_service )
127  url = sqlalchemy_tpl %(username,pwd,db_service)
128  session = None
129  try:
130  self.eng = sqlalchemy.create_engine( url, max_identifier_length=30)
131  session = sqlalchemy.orm.scoped_session( sqlalchemy.orm.sessionmaker(bind=self.eng))
132  except sqlalchemy.exc.SQLAlchemyError as dberror:
133  self.logger.error( str(dberror) )
134  return session
135 
136  def connect( self, service, args ):
137  self.session = self.getSession( service, args.role, args.auth )
138  self.verbose = args.verbose
139  if self.session is None:
140  return False
141  else:
142  self.db_connection = coral_tpl %(service,schema_name)
143  self.conf_dict['db']=self.db_connection
144  return True
145  def runManager( self ):
146  return O2ORunMgr( self.db_connection, self.session, self.logger )
147 
148  def add( self, job_name, configJson, int_val, freq_flag, en_flag ):
149  if configJson == '':
150  return False
151  res = self.session.query(O2OJob.enabled).filter_by(name=job_name)
152  enabled = None
153  for r in res:
154  enabled = r
155  if enabled:
156  self.logger.error( "A job called '%s' exists already.", job_name )
157  return False
158  freq_val = 0
159  if freq_flag:
160  freq_val = 1
161  job = O2OJob(name=job_name,tag_name='-',enabled=en_flag,frequent=freq_val,interval=int_val)
162  config = O2OJobConf( job_name=job_name, insertion_time = datetime.utcnow(), configuration = configJson )
163  self.session.add(job)
164  self.session.add(config)
165  self.session.commit()
166  self.logger.info( "New o2o job '%s' created.", job_name )
167  return True
168 
169  def set( self, job_name, en_flag, fr_val=None ):
170  res = self.session.query(O2OJob.enabled).filter_by(name=job_name)
171  enabled = None
172  for r in res:
173  enabled = r
174  if enabled is None:
175  self.logger.error( "A job called '%s' does not exist.", job_name )
176  return
177  if en_flag is not None and enabled != en_flag:
178  job = O2OJob(name=job_name,enabled=en_flag)
179  self.session.merge(job)
180  self.session.commit()
181  action = 'enabled'
182  if not en_flag:
183  action = 'disabled'
184  self.logger.info( "Job '%s' %s." %(job_name,action) )
185  if fr_val is not None:
186  job = O2OJob(name=job_name,frequent=fr_val)
187  self.session.merge(job)
188  self.session.commit()
189  if fr_val==1:
190  self.logger.info( "Job '%s' set 'frequent'" %job_name)
191  else:
192  self.logger.info( "Job '%s' unset 'frequent'" %job_name)
193 
194  def setConfig( self, job_name, configJson ):
195  if configJson == '':
196  return False
197  res = self.session.query(O2OJob.enabled).filter_by(name=job_name)
198  enabled = None
199  for r in res:
200  enabled = r
201  if enabled is None:
202  self.logger.error( "A job called '%s' does not exist.", job_name )
203  return
204  config = O2OJobConf( job_name=job_name, insertion_time = datetime.utcnow(), configuration = configJson )
205  self.session.add(config)
206  self.session.commit()
207  self.logger.info( "New configuration inserted for job '%s'", job_name )
208  return True
209 
210  def setInterval( self, job_name, int_val ):
211  res = self.session.query(O2OJob.enabled).filter_by(name=job_name)
212  enabled = None
213  for r in res:
214  enabled = r
215  if enabled is None:
216  self.logger.error( "A job called '%s' does not exist.", job_name )
217  return
218  job = O2OJob(name=job_name,interval=int_val)
219  self.session.merge(job)
220  self.session.commit()
221  self.logger.info( "The execution interval for job '%s' has been updated.", job_name )
222 
223  def listJobs( self ):
224  runs = {}
225  res = self.session.query(O2ORun.job_name,sqlalchemy.func.max(O2ORun.start_time)).group_by(O2ORun.job_name).order_by(O2ORun.job_name)
226  for r in res:
227  runs[r[0]] = str(r[1])
228  res = self.session.query(O2OJob.name, O2OJob.interval, O2OJob.enabled, O2OJob.frequent).order_by(O2OJob.name).all()
229  table = []
230  for r in res:
231  row = []
232  row.append(r[0]),
233  row.append('%5d' %r[1] )
234  frequent = 'Y' if (r[3]==1) else 'N'
235  row.append('%4s' %frequent )
236  enabled = 'Y' if (r[2]==1) else 'N'
237  row.append('%4s' %enabled )
238  lastRun = '-'
239  if r[0] in runs.keys():
240  lastRun = runs[r[0]]
241  row.append( lastRun )
242  table.append(row)
243  headers = ['Job name','Interval','Frequent','Enabled','Last run start']
244  print_table( headers, table )
245 
246  def listConfig( self, jname ):
247  res = self.session.query(O2OJob.enabled).filter_by(name=jname)
248  enabled = None
249  for r in res:
250  enabled = r
251  if enabled is None:
252  self.logger.error( "A job called '%s' does not exist.", jname )
253  return
254  res = self.session.query( O2OJobConf.configuration, O2OJobConf.insertion_time ).filter_by(job_name=jname).order_by(O2OJobConf.insertion_time)
255  configs = []
256  for r in res:
257  configs.append((str(r[0]),r[1]))
258  ind = len(configs)
259  if ind:
260  print("Configurations for job '%s'" %jname)
261  for cf in reversed(configs):
262  print('#%2d since: %s' %(ind,cf[1]))
263  print(cf[0])
264  ind -= 1
265  else:
266  self.logger.info("No configuration found for job '%s'" %jname )
267 
268  def dumpConfig( self, jname, versionIndex, configFile ):
269  versionIndex = int(versionIndex)
270  res = self.session.query(O2OJob.enabled).filter_by(name=jname)
271  enabled = None
272  for r in res:
273  enabled = r
274  if enabled is None:
275  self.logger.error( "A job called '%s' does not exist.", jname )
276  return
277  res = self.session.query( O2OJobConf.configuration, O2OJobConf.insertion_time ).filter_by(job_name=jname).order_by(O2OJobConf.insertion_time)
278  configs = []
279  for r in res:
280  configs.append((str(r[0]),r[1]))
281  ind = len(configs)
282  if versionIndex>ind or versionIndex==0:
283  self.logger.error("Configuration for job %s with index %s has not been found." %(jname,versionIndex))
284  return
285  print("Configuration #%2d for job '%s'" %(versionIndex,jname))
286  config = configs[versionIndex-1]
287  print('#%2d since %s' %(versionIndex,config[1]))
288  print(config[0])
289  if configFile is None or configFile == '':
290  configFile = '%s_%s.json' %(jname,versionIndex)
291  with open(configFile,'w') as json_file:
292  json_file.write(config[0])
293 
294 
296 
297  def __init__( self, db_connection, session, logger ):
298  self.job_name = None
299  self.start = None
300  self.end = None
301  self.conf_dict = {}
302  self.conf_dict['db'] = db_connection
303  self.session = session
304  self.logger = logger
305 
306  def startJob( self, job_name ):
307  self.logger.info('Checking job %s', job_name)
308  exists = None
309  enabled = None
310  try:
311  res = self.session.query(O2OJob.enabled,O2OJob.tag_name).filter_by(name=job_name)
312  for r in res:
313  exists = True
314  enabled = int(r[0])
315  self.tag_name = str(r[1])
316  if exists is None:
317  self.logger.error( 'The job %s is unknown.', job_name )
318  return 2
319  if enabled:
320  res = self.session.query(O2OJobConf.configuration).filter_by(job_name=job_name).order_by(sqlalchemy.desc(O2OJobConf.insertion_time)).first()
321  conf = None
322  for r in res:
323  conf = str(r)
324  if conf is None:
325  self.logger.warning("No configuration found for job '%s'" %job_name )
326  else:
327  try:
328  self.conf_dict.update( json.loads(conf) )
329  self.logger.info('Using configuration: %s ' %conf)
330  except Exception as e:
331  self.logger.error( str(e) )
332  return 6
333  self.job_name = job_name
334  self.start = datetime.utcnow()
335  run = O2ORun(job_name=self.job_name,start_time=self.start,status_code=startStatus)
336  self.session.add(run)
337  self.session.commit()
338  return 0
339  else:
340  self.logger.info( 'The job %s has been disabled.', job_name )
341  return 5
342  except sqlalchemy.exc.SQLAlchemyError as dberror:
343  self.logger.error( str(dberror) )
344  return 7
345  return -1
346 
347 
348  def endJob( self, status, log ):
349  self.end = datetime.utcnow()
350  try:
351  run = O2ORun(job_name=self.job_name,start_time=self.start,end_time=self.end,status_code=status,log=log)
352  self.session.merge(run)
353  self.session.commit()
354  self.logger.info( 'Job %s ended.', self.job_name )
355  return 0
356  except sqlalchemy.exc.SQLAlchemyError as dberror:
357  self.logger.error( str(dberror) )
358  return 8
359 
360  def executeJob( self, args ):
361  job_name = args.name
362  command = args.executable
363  logFolder = os.getcwd()
364  if logFolderEnvVar in os.environ:
365  logFolder = os.environ[logFolderEnvVar]
366  datelabel = datetime.utcnow().strftime("%y-%m-%d-%H-%M-%S")
367  logFileName = '%s-%s.log' %(job_name,datelabel)
368  logFile = os.path.join(logFolder,logFileName)
369  started = self.startJob( job_name )
370  if started !=0:
371  return started
372  ret = -1
373  try:
374  # replacing %([key])s placeholders...
375  command = command %(self.conf_dict)
376  #replacing {[key]} placeholders
377  command = command.format(**self.conf_dict )
378  except KeyError as exc:
379  self.logger.error( "Unresolved template key %s in the command." %str(exc) )
380  return 3
381  self.logger.info('Command: "%s"', command )
382  out = ''
383  try:
384  self.logger.info('Executing command...' )
385  pipe = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
386  for line in pipe.stdout:
387  if args.verbose is not None and args.verbose>=1:
388  sys.stdout.write(line.decode())
389  sys.stdout.flush()
390  out += line.decode()
391  pipe.communicate()
392  self.logger.info( 'Command returned code: %s' %pipe.returncode )
393  ret = pipe.returncode
394  except Exception as e:
395  self.logger.error( str(e) )
396  return 4
397  ended = self.endJob( pipe.returncode, out )
398  if ended != 0:
399  ret = ended
400  with open(logFile,'a') as logF:
401  logF.write(out)
402  return ret
403 
404 def readConfiguration( config_filename ):
405  config = ''
406  try:
407  with open( config_filename, 'r' ) as config_file:
408  config = config_file.read().strip('\n')
409  if config == '':
410  logging.error( 'The file %s contains an empty string.', config_filename )
411  else:
412  json.loads(config)
413  except IOError as e:
414  logging.error( 'The file %s cannot be open.', config_filename )
415  except ValueError as e:
416  config = ''
417  logging.error( 'The file "%s" contains an invalid json string.', config_filename )
418  return config
419 
420 def checkConfiguration( config_string ):
421  config = config_string
422  try:
423  json.loads(config)
424  except ValueError as e:
425  config = ''
426  logging.error( 'The string "%s" is an invalid json format.', config_string )
427  return config
428 
429 
430 import optparse
431 import argparse
432 
433 class O2OTool():
434 
435  def execute(self):
436  parser = argparse.ArgumentParser(description='CMS o2o command-line tool. For general help (manual page), use the help subcommand.')
437  parser.add_argument('--db', type=str, help='The target database: pro ( for prod ) or dev ( for prep ). default=pro')
438  parser.add_argument("--auth","-a", type=str, help="The path of the authentication file")
439  parser.add_argument('--verbose', '-v', action='count', help='The verbosity level')
440  parser_subparsers = parser.add_subparsers(title='Available subcommands')
441  parser_create = parser_subparsers.add_parser('create', description='Create a new O2O job')
442  parser_create.add_argument('--name', '-n', type=str, help='The o2o job name',required=True)
443  parser_create.add_argument('--configFile', '-c', type=str, help='the JSON configuration file path')
444  parser_create.add_argument('--configString', '-s', type=str, help='the JSON configuration string')
445  parser_create.add_argument('--interval', '-i', type=int, help='the chron job interval',default=0)
446  parser_create.add_argument('--frequent', '-f',action='store_true',help='set the "frequent" flag for this job ("false" by default)')
447  parser_create.set_defaults(func=self.create,role=auth.admin_role)
448  parser_setConfig = parser_subparsers.add_parser('setConfig', description='Set a new configuration for the specified job. The configuration is expected as a list of entries "param": "value" (dictionary). The "param" labels will be used to inject the values in the command to execute. The dictionary is stored in JSON format.')
449  parser_setConfig.add_argument('--name', '-n', type=str, help='The o2o job name',required=True)
450  parser_setConfig.add_argument('--configFile', '-c', type=str, help='the JSON configuration file path')
451  parser_setConfig.add_argument('--configString', '-s', type=str, help='the JSON configuration string')
452  parser_setConfig.set_defaults(func=self.setConfig,role=auth.admin_role)
453  parser_setFrequent = parser_subparsers.add_parser('setFrequent',description='Set the "frequent" flag for the specified job')
454  parser_setFrequent.add_argument('--name', '-n', type=str, help='The o2o job name',required=True)
455  parser_setFrequent.add_argument('--flag', '-f', choices=['0','1'], help='the flag value to set',required=True)
456  parser_setFrequent.set_defaults(func=self.setFrequent,role=auth.admin_role)
457  parser_setInterval = parser_subparsers.add_parser('setInterval',description='Set a new execution interval for the specified job')
458  parser_setInterval.add_argument('--name', '-n', type=str, help='The o2o job name',required=True)
459  parser_setInterval.add_argument('--interval', '-i', type=int, help='the chron job interval',required=True)
460  parser_setInterval.set_defaults(func=self.setInterval,role=auth.admin_role)
461  parser_enable = parser_subparsers.add_parser('enable',description='enable the O2O job')
462  parser_enable.add_argument('--name', '-n', type=str, help='The o2o job name',required=True)
463  parser_enable.set_defaults(func=self.enable,role=auth.admin_role)
464  parser_disable = parser_subparsers.add_parser('disable',description='disable the O2O job')
465  parser_disable.add_argument('--name', '-n', type=str, help='The o2o job name',required=True)
466  parser_disable.set_defaults(func=self.disable,role=auth.admin_role)
467  parser_listJobs = parser_subparsers.add_parser('listJobs', description='list the registered jobs')
468  parser_listJobs.set_defaults(func=self.listJobs,role=auth.reader_role)
469  parser_listConf = parser_subparsers.add_parser('listConfig', description='shows the configurations for the specified job')
470  parser_listConf.add_argument('--name', '-n', type=str, help='The o2o job name',required=True)
471  parser_listConf.add_argument('--dump', type=int, help='Dump the specified config.',default=0)
472  parser_listConf.set_defaults(func=self.listConf,role=auth.reader_role)
473  parser_dumpConf = parser_subparsers.add_parser('dumpConfig', description='dumps a specific job configuration version')
474  parser_dumpConf.add_argument('versionIndex', type=str,help='the version to dump')
475  parser_dumpConf.add_argument('--name', '-n', type=str, help='The o2o job name',required=True)
476  parser_dumpConf.add_argument('--configFile', '-c', type=str, help='the JSON configuration file name - default:[jobname]_[version].json')
477  parser_dumpConf.set_defaults(func=self.dumpConf,role=auth.reader_role)
478  parser_run = parser_subparsers.add_parser('run', description='Wrapper for O2O jobs execution. Supports input parameter injection from the configuration file associated to the job. The formatting syntax supported are the python ones: "command -paramName {paramLabel}" or "command -paramName %(paramLabel)s". where [paramName] is the name of the parameter required for the command, and [paramLabel] is the key of the parameter entry in the config dictionary (recommended to be equal for clarity!"')
479  parser_run.add_argument('executable', type=str,help='command to execute')
480  parser_run.add_argument('--name', '-n', type=str, help='The o2o job name',required=True)
481  parser_run.set_defaults(func=self.run,role=auth.writer_role)
482 
483  args = parser.parse_args()
484 
485  if args.verbose is not None and args.verbose >=1:
486  self.setup(args)
487  return args.func()
488  else:
489  try:
490  self.setup(args)
491  sys.exit( args.func())
492  except Exception as e:
493  logging.error(e)
494  sys.exit(1)
495 
496  def setup(self, args):
497  self.args = args
498  db_service = prod_db_service
499  if args.db is not None:
500  if args.db == 'dev' or args.db == 'oradev' :
501  db_service = dev_db_service
502  elif args.db != 'orapro' and args.db != 'onlineorapro' and args.db != 'pro':
503  raise Exception("Database '%s' is not known." %args.db )
504 
505  logLevel = logging.DEBUG if args.verbose is not None and args.verbose >= 1 else logging.INFO
506  self.mgr = O2OJobMgr( logLevel )
507  return self.mgr.connect( db_service, args )
508 
509  def create(self):
510  configJson = None
511  if self.args.configFile is not None:
512  if self.args.configString is not None:
513  logging.error('Ambigouous input provided: please specify a configFile OR a configString')
514  return False
515  else:
516  configJson = readConfiguration( self.args.configFile )
517  else:
518  if self.args.configString is None:
519  logging.error('No configuration has been provided: please specify "configFile" or "configString" param.')
520  return False
521  else:
522  configJson = checkConfiguration( self.args.configString )
523  self.mgr.add( self.args.name, configJson, self.args.interval, self.args.frequent, True )
524 
525  def setConfig(self):
526  configJson = None
527  if self.args.configFile is not None:
528  if self.args.configString is not None:
529  logging.error('Ambigouous input provided: please specify a configFile OR a configString')
530  return False
531  else:
532  configJson = readConfiguration( self.args.configFile )
533  else:
534  if self.args.configString is None:
535  logging.error('No configuration has been provided: please specify "configFile" or "configString" param.')
536  return False
537  else:
538  configJson = checkConfiguration( self.args.configString )
539  self.mgr.setConfig( self.args.name, configJson )
540 
541  def setInterval(self):
542  self.mgr.setInterval( self.args.name, self.args.interval )
543 
544  def enable(self):
545  self.mgr.set( self.args.name, True )
546 
547  def disable(self):
548  self.mgr.set( self.args.name, False )
549 
550  def setFrequent(self):
551  self.mgr.set( self.args.name, None, int(self.args.flag) )
552 
553  def listJobs(self):
554  self.mgr.listJobs()
555 
556  def listConf(self):
557  self.mgr.listConfig( self.args.name )
558 
559  def dumpConf(self):
560  self.mgr.dumpConfig( self.args.name, self.args.versionIndex, self.args.configFile )
561 
562  def run(self):
563  rmgr = self.mgr.runManager()
564  return rmgr.executeJob( self.args )
Definition: merge.py:1
def listJobs(self)
Definition: o2olib.py:223
def disable(self)
Definition: o2olib.py:547
def listJobs(self)
Definition: o2olib.py:553
def setup(self, args)
Definition: o2olib.py:496
static const TGPicture * info(bool iBackgroundIsBlack)
def set(self, job_name, en_flag, fr_val=None)
Definition: o2olib.py:169
def all(container)
workaround iterator generators for ROOT classes
Definition: cmstools.py:25
def checkConfiguration(config_string)
Definition: o2olib.py:420
def __init__(self, logLevel)
Definition: o2olib.py:91
def endJob(self, status, log)
Definition: o2olib.py:348
def dumpConf(self)
Definition: o2olib.py:559
def listConfig(self, jname)
Definition: o2olib.py:246
def add(self, job_name, configJson, int_val, freq_flag, en_flag)
Definition: o2olib.py:148
def setInterval(self, job_name, int_val)
Definition: o2olib.py:210
Definition: query.py:1
def setConfig(self, job_name, configJson)
Definition: o2olib.py:194
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
def setInterval(self)
Definition: o2olib.py:541
def setFrequent(self)
Definition: o2olib.py:550
def runManager(self)
Definition: o2olib.py:145
def getSession(self, db_service, role, authPath)
Definition: o2olib.py:108
def connect(self, service, args)
Definition: o2olib.py:136
def executeJob(self, args)
Definition: o2olib.py:360
#define debug
Definition: HDRShower.cc:19
def startJob(self, job_name)
Definition: o2olib.py:306
def run(self)
Definition: o2olib.py:562
void add(std::map< std::string, TH1 *> &h, TH1 *hist)
#define update(a, b)
def __init__(self, db_connection, session, logger)
Definition: o2olib.py:297
def enable(self)
Definition: o2olib.py:544
def dumpConfig(self, jname, versionIndex, configFile)
Definition: o2olib.py:268
def print_table(headers, table)
Definition: o2olib.py:58
def readConfiguration(config_filename)
Definition: o2olib.py:404
#define str(s)
def create(self)
Definition: o2olib.py:509
def setConfig(self)
Definition: o2olib.py:525
def listConf(self)
Definition: o2olib.py:556
def execute(self)
Definition: o2olib.py:435