#!/usr/bin/env python # # Author: Sean Dobbs (s-dobbs@northwestern.edu), 2014-03-05 # # Original Version: # Copyright 2008 Cornell University, Ithaca, NY 14853. All rights reserved. # Author: Valentin Kuznetsov, 2008 """ EventSource MetaData web server It depends on - cElementTree for parsing SOAP/XML, http://effbot.org/zone/element-index.htm - cherrypy for web server, http://www.cherrypy.org - SQLAlchemy for SQL access, http://www.sqlalchemy.org """ import string, os, sys, re, types import xml.sax, xml.sax.handler from xml.sax.saxutils import escape import cherrypy from cherrypy import expose import sqlalchemy from sqlalchemy.sql import select import cElementTree as et import elementtree.ElementTree as ET import logging, logging.handlers from optparse import OptionParser import ConfigParser def setCherryPyLogger(hdlr,logLevel): # set up logging for SQLAlchemy logging.getLogger('cherrypy.error').setLevel(logLevel) logging.getLogger('cherrypy.access').setLevel(logLevel) logging.getLogger('cherrypy.error').addHandler(hdlr) logging.getLogger('cherrypy.access').addHandler(hdlr) def setSQLAlchemyLogger(hdlr,logLevel): # set up logging for SQLAlchemy logging.getLogger('sqlalchemy.engine').setLevel(logLevel) logging.getLogger('sqlalchemy.orm.unitofwork').setLevel(logLevel) logging.getLogger('sqlalchemy.pool').setLevel(logLevel) logging.getLogger('sqlalchemy.engine').addHandler(hdlr) logging.getLogger('sqlalchemy.orm.unitofwork').addHandler(hdlr) logging.getLogger('sqlalchemy.pool').addHandler(hdlr) ############################################################################### ### class DBManager ############################################################################### class DBManager(object): def __init__(self, dbuser, dbpass, dbhost, dbname): self.engine = None # Initialize SQLAlchemy engines eName = "mysql://%s:%s@%s/%s"%(dbuser, dbpass, dbhost, dbname) self.engine = sqlalchemy.create_engine(eName, strategy='threadlocal') self.tables = {} meta = sqlalchemy.MetaData() meta.bind = self.engine tables = [] tlist = self.engine.table_names() for t in tlist: self.tables[t]=sqlalchemy.Table(t, meta, autoload=True) def getruns(self, name, erange=None): query = """ SELECT DISTINCT r.run_number FROM Raw_Run_Properties r, Dataset d, Energy_Class e WHERE r.run_number is not null """ if name: query += """ AND d.dataset_name = :dname AND r.run_number >= d.first_run_number AND r.run_number <= d.last_run_number """ if erange: query += """ AND e.energy_range_name = :erange AND r.beam_energy >= e.min_beam_energy AND r.beam_energy <= e.max_beam_energy """ query += "ORDER BY r.run_number ASC" sel = sqlalchemy.text(query) con = self.engine.connect() result = con.execute(sel, dname=name, erange=erange) runs = (int(run[0]) for run in result) con.close() return runs def getenergynames(self): query = """SELECT d.energy_range_name FROM Energy_Class d WHERE d.energy_range_name is not null ORDER BY d.energy_range_name ASC""" sel = sqlalchemy.text(query) con = self.engine.connect() result = con.execute(sel) names = (name[0] for name in result) con.close() return names def getdatasetnames(self): query = """SELECT d.dataset_name FROM Dataset d WHERE d.dataset_name is not null ORDER BY d.dataset_name ASC""" sel = sqlalchemy.text(query) con = self.engine.connect() result = con.execute(sel) names = (name[0] for name in result) con.close() return names def getrunranges(self, name, erange): query = """SELECT first_run_number, last_run_number FROM Dataset WHERE dataset_name = :dname""" sel = sqlalchemy.text(query) con = self.engine.connect() result = con.execute(sel, dname=name) runs = ((int(run[0]), int(run[1])) for run in result) con.close() return runs def getboundaries(self, ename): query = """SELECT energy_range_name, min_beam_energy, max_beam_energy FROM Energy_Class WHERE energy_range_name = :ename""" sel = sqlalchemy.text(query) con = self.engine.connect() result = con.execute(sel, ename=ename) bounds = ((item[0], float(item[1]), float(item[2])) for item in result) con.close() return bounds def getproperties(self, srun, erun): query = """SELECT run_number, start_time, event_count, last_event_number, luminosity, beam_energy, magnetic_field, run_type, runmanager_approved FROM Raw_Run_Properties WHERE run_number >= :r1 and run_number <= :r2""" sel = sqlalchemy.text(query) con = self.engine.connect() result = con.execute(sel, r1=srun, r2=erun) runs = ((int(i[0]), i[1], int(i[2]), int(i[3]), \ float(i[4]), float(i[5]), float(i[6]), i[7], i[8] ) \ for i in result) con.close() return runs ############################################################################### ### class ESMetaDataServer ############################################################################### class ESMetaDataServer(object): def __init__(self,dbmgr, logName='/tmp/esmetadb-cmds.log',verbose=0): self.dbmgr = dbmgr self.verbose=verbose hdlr = logging.handlers.TimedRotatingFileHandler( logName, 'midnight', 1, 7 ) formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) hdlr.setFormatter( formatter ) self.logger = logging.getLogger("CMDServer") if verbose: logLevel=logging.DEBUG else: logLevel=logging.INFO self.logger.setLevel(logLevel) self.logger.addHandler(hdlr) setSQLAlchemyLogger(hdlr,logLevel) setCherryPyLogger(hdlr,logLevel) def writeLog(self,_msg): if type(_msg) is not types.StringType: msg=repr(_msg) else: msg=_msg if self.verbose: self.logger.debug(msg) else: self.logger.info(msg) @expose def index(self,**kwargs): self.namespace_expr = re.compile(r'^\{.*\}') # get request data and produce an ElementTree that we can work with. request = cherrypy.request if self.verbose > 1: self.writeLog(request) response = cherrypy.response if not request.headers.has_key('Content-Length'): page = "
The following operations are supported. * GetEnergyBoundaries(Energy_Range_Name) Get energy boundaries for a given energy name. * GetEnergyRangeNames() Query for a list of valid energy range names. * GetRuns(Dataset_Name, Energy_Range_Name) Query by dataset and energy range to return a list of runs. Both parameters are optional. * GetRawRunProperties(Start_Run_Number, End_Run_Number) Query and return an array of Raw_Run_Properties. * GetDatasetNames() Get a list of valid dataset names. * GetRunRanges(Dataset_Name, Energy_Range_Name) Query by dataset and energy range to return a list of run ranges. Both parameters are optional.""" return page clen = int(request.headers.get('Content-Length')) or 0 data = request.body.read(clen) if self.verbose > 1: self.writeLog(data) request.soap_start = data[:2048] soapreq = et.fromstring(data) #self.writeLog(soapreq) # find the body of the request and the specific method name that has # been requested. body = soapreq.find("{http://schemas.xmlsoap.org/soap/envelope/}Body") #self.writeLog("body0="+repr(body)) body = body.getchildren()[0] #self.writeLog("body1="+repr(body)) methodname = body.tag methodname = self.namespace_expr.sub("", methodname) #self.writeLog(methodname) request.soap_method = methodname method=getattr(self,methodname) if self.verbose > 1: self.writeLog(method) params = {"_ws" : True} params["xml_body"] = body return method(**params) def setContentType(self,type): """ Set CherryPy Content-Type to provided type @type type: string @param type: type of application, "text/xml" or "text/html" @rtype: none @return: none """ if int(string.split(cherrypy.__version__,".")[0])==3: if type=="xml": cherrypy.response.headers['Content-Type']='text/xml;charset=utf-8' else: cherrypy.response.headers['Content-Type']='text/html' elif int(string.split(cherrypy.__version__,".")[0])==2: if type=="xml": cherrypy.response.headerMap['Content-Type'] = "text/xml;charset=utf-8" else: cherrypy.response.headerMap['Content-Type'] = "text/html" def soapTopEnvelop(self): page="""