Package glue :: Module LDBDServer
[hide private]
[frames] | no frames]

Source Code for Module glue.LDBDServer

  1  """ 
  2  The LDBDServer module provides an API for responding to request from the 
  3  LDBDClient by connecting to the DB2 database. 
  4   
  5  This module requires U{pyGlobus<http://www-itg.lbl.gov/gtg/projects/pyGlobus/>}. 
  6   
  7   
  8  This file is part of the Grid LSC User Environment (GLUE) 
  9   
 10  GLUE is free software: you can redistribute it and/or modify it under the 
 11  terms of the GNU General Public License as published by the Free Software 
 12  Foundation, either version 3 of the License, or (at your option) any later 
 13  version. 
 14   
 15  This program is distributed in the hope that it will be useful, but WITHOUT 
 16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
 17  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
 18  details. 
 19   
 20  You should have received a copy of the GNU General Public License along with 
 21  this program.  If not, see <http://www.gnu.org/licenses/>. 
 22  """ 
 23   
 24  from glue import git_version 
 25  __date__ = git_version.date 
 26  __version__ = git_version.id 
 27   
 28  import os 
 29  import sys 
 30  import re 
 31  import types 
 32  try: 
 33      import pyRXP 
 34  except ImportError: 
 35      import pyRXPU as pyRXP 
 36  import socket 
 37  import six.moves.socketserver 
 38  import six.moves.cPickle 
 39  from glue import ldbd 
 40  try: 
 41    import rlsClient 
 42  except: 
 43    pass 
 44   
 45   
46 -def dtd_uri_callback(uri):
47 if uri == 'http://ldas-sw.ligo.caltech.edu/doc/ligolwAPI/html/ligolw_dtd.txt': 48 # if the XML file contants a http pointer to the ligolw DTD at CIT then 49 # return a local copy to avoid any network problems 50 return 'file://localhost' + os.path.join( os.environ["GLUE_PREFIX"], 51 'etc/ligolw_dtd.txt' ) 52 else: 53 # otherwise just use the uri in the file 54 return uri
55
56 -def initialize(configuration,log):
57 # define the global variables used by the server 58 global logger, max_bytes, xmlparser, dbobj, rls 59 global dmt_proc_dict, dmt_seg_def_dict, creator_db 60 global ldbd_com, db2_com, port 61 62 # initialize the logger 63 logger = log 64 logger.info("Initializing server module %s" % __name__ ) 65 66 # initialize the database hash table 67 dbobj = ldbd.LIGOMetadataDatabase(configuration['dbname']) 68 max_bytes = configuration['max_client_byte_string'] 69 70 # initialize the ldbd commands and db2 command restrictions 71 port = configuration['port'] 72 ldbd_com = configuration['ldbd_com'].split(',') 73 db2_com = configuration['db2_com'].split(',') 74 75 # create the xml parser 76 xmlparser = pyRXP.Parser() 77 78 # use a local copy of the DTD, if one is available 79 try: 80 GLUE_PREFIX = os.environ["GLUE_PREFIX"] 81 xmlparser.eoCB = dtd_uri_callback 82 logger.info("Using local DTD in " + 83 'file://localhost' + os.path.join( GLUE_PREFIX, 'etc') ) 84 except KeyError: 85 logger.warning('GLUE_PREFIX not set, unable to use local DTD') 86 87 # open a connection to the rls server 88 rls_server = configuration['rls'] 89 cert = configuration['certfile'] 90 key = configuration['keyfile'] 91 try: 92 rls = rlsClient.RlsClient(rls_server,cert,key) 93 except: 94 rls = None 95 96 # initialize dictionaries for the dmt processes and segments definers 97 dmt_proc_dict = {} 98 dmt_seg_def_dict = {} 99 creator_db = None
100
101 -def shutdown():
102 global logger, max_bytes, xmlparser, dbobj, rls 103 global dmt_proc_dict, dmt_seg_def_dict 104 logger.info("Shutting down server module %s" % __name__ ) 105 if rls: 106 del rls 107 del xmlparser 108 del dbobj 109 del dmt_proc_dict 110 del dmt_seg_def_dict
111
112 -class ServerHandlerException(Exception):
113 """ 114 Class representing exceptions within the ServerHandler class. 115 """
116 - def __init__(self, args=None):
117 """ 118 Initialize an instance. 119 120 @param args: 121 122 @return: Instance of class ServerHandlerException 123 """ 124 self.args = args
125
126 -class ServerHandler(six.moves.socketserver.BaseRequestHandler):
127 """ 128 An instance of this class is created to service each request of the server. 129 """
130 - def handle(self):
131 """ 132 This method does all the work of servicing a request to the server. See 133 the documentation for the standard module SocketServer. 134 135 The input from the socket is parsed for the method with the remaining 136 strings stripped of null bytes passed to the method in a list. 137 138 There are no parameters. When the instance of the class is created to 139 process the request all necessary information is made attributes of the 140 class instance. 141 142 @return: None 143 """ 144 global logger 145 global max_bytes 146 147 logger.debug("handle method of %s class called" % __name__) 148 149 # mapping of ldbdd RPC protocol names to methods of this class 150 methodDict = { 151 'PING' : self.ping, 152 'QUERY' : self.query, 153 'INSERT' : self.insert, 154 'INSERTMAP' : self.insertmap, 155 'INSERTDMT' : self.insertdmt 156 } 157 158 if True: 159 # from the socket object create a file object 160 self.sfile = self.request.makefile("rw") 161 f = self.sfile 162 163 # read all of the input up to limited number of bytes 164 input = f.read(size=max_bytes,waitForBytes=2) 165 166 # try 10 more times if we don't have a null byte at the end 167 while input[-1] != '\0': 168 input += f.read(size=max_bytes,waitForBytes=2) 169 170 # the format should be a method string, followed by a null byte 171 # followed by the arguments to the method encoded as null 172 # terminated strings 173 174 # check if the last byte is a null byte 175 if input[-1] != '\0': 176 logger.error("Bad input on socket: %s" % input) 177 raise ServerHandlerException("Last byte of input is not null byte") 178 #except Exception, e: 179 # logger.error("Error reading input on socket: %s" % e) 180 # return 181 182 try: 183 # parse out the method and arguments 184 stringList = input.split('\0') 185 methodString = stringList[0] 186 argStringList = stringList[1:-1] 187 188 except Exception as e: 189 logger.error("Error parsing method and argument string: %s" % e) 190 191 msg = "ERROR LDBDServer Error: " + \ 192 "Error parsing method and argument string: %s" % e 193 self.__reply__(1, msg) 194 return 195 196 # Set read and read/write access to different ports according to their configuration file 197 try: 198 # ldbd_com lists the allowed methodString for ldbd server based on the port number it is running on 199 if methodString in ldbd_com: 200 pass 201 else: 202 msg = '\nTo %s, authorized user please switch to the Apache driven ldbd server through ' % methodString 203 msg += '\nsecure connection by specifying procotol "https" in your --segment-url argument' 204 msg += '\nFor example, "--segment-url https://segdb.ligo.caltech.edu"' 205 logger.error(msg) 206 self.__reply__(1,msg) 207 raise ServerHandlerException(msg) 208 # list allowed sql commands when methodString is QUERY 209 if methodString=='QUERY': 210 # get the sql command, for example "SELECT, UPDATE, INSERT" 211 # see if the command is in the .ini file "db2_com" variable 212 dbcommand = argStringList[0].split(' ')[0].upper() 213 if dbcommand in db2_com: 214 pass 215 else: 216 msg = 'ldbd server on port %d DO NOT support "%s"' % (port, dbcommand) 217 logger.error(msg) 218 self.__reply__(1,msg) 219 raise ServerHandlerException(msg) 220 except Exception as e: 221 logger.error("Error filtering allowed commands: %s" % e) 222 return 223 224 225 try: 226 # look up method in dictionary 227 method = methodDict[methodString] 228 except Exception as e: 229 msg = "Error converting method string %s to method call: %s" % \ 230 (methodString, e) 231 logger.error(msg) 232 233 self.__reply__(1, msg) 234 return 235 236 try: 237 # call the method requested with the rest of strings as input 238 result = method(argStringList) 239 self.__reply__( result[0], result[1] ) 240 except Exception as e: 241 logger.error("Error while calling method %s: %s" % (methodString, e)) 242 243 return
244
245 - def __reply__(self, code, msg):
246 """ 247 Format and send a reply back down the socket to the client. The file 248 representing the socket is closed at the end of this method. 249 250 @param code: integer representing the error code with 0 for success 251 252 @param msg: object to be passed back to the client, either a string 253 or a list of items that can be represented by strings 254 255 @return: None 256 """ 257 f = self.sfile 258 reply = "%d\0%s\0" % (code, msg) 259 f.write(reply) 260 261 # close the file associated with the socket 262 f.close()
263
264 - def ping(self, arg):
265 """ 266 Bounce back alive statment. Corresponds to the PING method in the 267 ldbdd RPC protocol. 268 269 @param arg: list (perhaps empty) of strings representing message sent 270 by client to server 271 272 @return: None 273 """ 274 global logger 275 276 logger.debug("Method ping called") 277 try: 278 hostname = socket.getfqdn() 279 msg = "%s at %s is alive" % (__name__, hostname) 280 except Exception as e: 281 msg = "%s is alive" % __name__ 282 283 return (0, msg)
284
285 - def query(self, arg):
286 """ 287 Execute an SQL query on the database and return the result as LIGO_LW XML 288 289 @param arg: a text string containing an SQL query to be executed 290 291 @return: None 292 """ 293 global logger 294 global xmlparser, dbobj 295 296 # get the query string and log it 297 querystr = arg[0] 298 logger.debug("Method query called with %s" % querystr) 299 300 # assume failure 301 code = 1 302 303 try: 304 # create a ligo metadata object 305 lwtparser = ldbd.LIGOLwParser() 306 ligomd = ldbd.LIGOMetadata(xmlparser,lwtparser,dbobj) 307 308 # execute the query 309 rowcount = ligomd.select(querystr) 310 311 # convert the result to xml 312 result = ligomd.xml() 313 314 logger.debug("Method query: %d rows returned" % rowcount) 315 code = 0 316 except Exception as e: 317 result = ("Error querying metadata database: %s" % e) 318 logger.error(result) 319 320 try: 321 del ligomd 322 del lwtparser 323 except Exception as e: 324 logger.error( 325 "Error deleting metadata object in method query: %s" % e) 326 327 return (code,result)
328
329 - def insert(self, arg):
330 msg = '\nTo INSERT, authorized user please switch to the Apache driven ldbd server through ' 331 msg += '\nsecure connection by specifying procotol "https" in your --segment-url argument' 332 msg += '\nFor example, "--segment-url https://segdb.ligo.caltech.edu"' 333 logger.error(msg) 334 return (1, msg)
335 336
337 - def insertmap(self, arg):
338 msg = '\nTo INSERTMAP, authorized user please switch to the Apache driven ldbd server through ' 339 msg += '\nsecure connection by specifying procotol "https" in your --segment-url argument' 340 msg += '\nFor example, "--segment-url https://segdb.ligo.caltech.edu"' 341 logger.error(msg) 342 return (1, msg)
343
344 - def insertdmt(self, arg):
345 msg = '\nTo INSERTDMT, authorized user please switch to the Apache driven ldbd server through ' 346 msg += '\nsecure connection by specifying procotol "https" in your --segment-url argument' 347 msg += '\nFor example, "--segment-url https://segdb.ligo.caltech.edu"' 348 logger.error(msg) 349 return (1, msg)
350