PageRenderTime 14ms CodeModel.GetById 1ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/scripts/galaxy_messaging/server/amqp_consumer.py

https://bitbucket.org/cistrome/cistrome-harvard/
Python | 164 lines | 128 code | 18 blank | 18 comment | 12 complexity | 6b91ec8bfa4738f8b0112040fcf26df0 MD5 | raw file
  1'''
  2Galaxy Messaging with AMQP (RabbitMQ)
  3Galaxy uses AMQ protocol to receive messages from external sources like
  4bar code scanners. Galaxy has been tested against RabbitMQ AMQP implementation.
  5For Galaxy to receive messages from a message queue the RabbitMQ server has
  6to be set up with a user account and other parameters listed in the [galaxy_amqp]
  7section in the universe_wsgi.ini config file
  8Once the RabbitMQ server has been setup and started with the given parameters,
  9this script can be run to receive messages and update the Galaxy database accordingly
 10'''
 11
 12import ConfigParser
 13import sys, os
 14import optparse
 15import xml.dom.minidom
 16import subprocess
 17import urllib2
 18
 19from xml_helper import get_value, get_value_index
 20
 21from galaxydb_interface import GalaxyDbInterface
 22
 23api_path = [ os.path.join( os.getcwd(), "scripts/api" ) ]
 24sys.path.extend( api_path )
 25import common as api
 26
 27assert sys.version_info[:2] >= ( 2, 4 )
 28new_path = [ os.path.join( os.getcwd(), "lib" ) ]
 29new_path.extend( sys.path[1:] ) # remove scripts/ from the path
 30sys.path = new_path
 31
 32from galaxy import eggs
 33from galaxy.web.api.requests import RequestsAPIController
 34import pkg_resources
 35pkg_resources.require( "amqp" )
 36import amqp
 37
 38import logging
 39log = logging.getLogger("GalaxyAMQP")
 40log.setLevel(logging.DEBUG)
 41fh = logging.FileHandler("galaxy_listener.log")
 42fh.setLevel(logging.DEBUG)
 43formatter = logging.Formatter("%(asctime)s - %(name)s - %(message)s")
 44fh.setFormatter(formatter)
 45log.addHandler(fh)
 46
 47# data transfer script
 48data_transfer_script = os.path.join( os.getcwd(),
 49                                     "scripts/galaxy_messaging/server/data_transfer.py" )
 50global config
 51global config_file_name
 52global http_server_section
 53
 54
 55def start_data_transfer( message ):
 56    # fork a new process to transfer datasets
 57    cmd = '%s "%s" "%s" "%s"' % ( "python",
 58                                  data_transfer_script,
 59                                  message.body,
 60                                  config_file_name ) # Galaxy config file name
 61    pid = subprocess.Popen(cmd, shell=True).pid
 62    log.debug('Started process (%i): %s' % (pid, str(cmd)))
 63
 64def update_sample_state( message ):
 65    dom = xml.dom.minidom.parseString(message.body)
 66    barcode = get_value(dom, 'barcode')
 67    state = get_value(dom, 'state')
 68    api_key = get_value(dom, 'api_key')
 69    log.debug('Barcode: ' + barcode)
 70    log.debug('State: ' + state)
 71    log.debug('API Key: ' + api_key)
 72    # validate
 73    if not barcode or not state or not api_key:
 74        log.debug( 'Incomplete sample_state_update message received. Sample barcode, desired state and user API key is required.' )
 75        return
 76    # update the sample state in Galaxy db
 77    dbconnstr = config.get("app:main", "database_connection")
 78    galaxydb = GalaxyDbInterface( dbconnstr )
 79    sample_id = galaxydb.get_sample_id( field_name='bar_code', value=barcode )
 80    if sample_id == -1:
 81       log.debug( 'Invalid barcode.' )
 82       return
 83    galaxydb.change_state(sample_id, state)
 84    # after updating the sample state, update request status
 85    request_id = galaxydb.get_request_id(sample_id)
 86    update_request( api_key, request_id )
 87
 88def update_request( api_key, request_id ):
 89    encoded_request_id = api.encode_id( config.get( "app:main", "id_secret" ), request_id )
 90    data = dict( update_type=RequestsAPIController.update_types.REQUEST )
 91    url = "http://%s:%s/api/requests/%s" % ( config.get(http_server_section, "host"),
 92                                             config.get(http_server_section, "port"),
 93                                             encoded_request_id )
 94    log.debug( 'Updating request %i' % request_id )
 95    try:
 96        retval = api.update( api_key, url, data, return_formatted=False )
 97        log.debug( str( retval ) )
 98    except Exception, e:
 99        log.debug( 'ERROR(update_request (%s)): %s' % ( str((self.api_key, url, data)), str(e) ) )
100
101def recv_callback( message ):
102    # check the meesage type.
103    msg_type = message.properties['application_headers'].get('msg_type')
104    log.debug( 'MESSAGE RECVD: ' + str( msg_type ) )
105    if msg_type == 'data_transfer':
106        log.debug( 'DATA TRANSFER' )
107        start_data_transfer( message )
108    elif msg_type == 'sample_state_update':
109        log.debug( 'SAMPLE STATE UPDATE' )
110        update_sample_state( message )
111
112def main():
113    parser = optparse.OptionParser()
114    parser.add_option('-c', '--config-file', help='Galaxy configuration file',
115                      dest='config_file', action='store')
116    parser.add_option('-s', '--http-server-section', help='Name of the HTTP server section in the Galaxy configuration file',
117                      dest='http_server_section', action='store')
118    (opts, args) = parser.parse_args()
119    log.debug( "GALAXY LISTENER PID: " + str(os.getpid()) + " - " + str( opts ) )
120    # read the Galaxy config file
121    global config_file_name
122    config_file_name = opts.config_file
123    global config
124    config = ConfigParser.ConfigParser()
125    config.read( opts.config_file )
126    global http_server_section
127    http_server_section = opts.http_server_section
128    amqp_config = {}
129    for option in config.options("galaxy_amqp"):
130        amqp_config[option] = config.get("galaxy_amqp", option)
131    log.debug( str( amqp_config ) )
132    # connect
133    conn = amqp.Connection(host=amqp_config['host']+":"+amqp_config['port'],
134                           userid=amqp_config['userid'],
135                           password=amqp_config['password'],
136                           virtual_host=amqp_config['virtual_host'])
137    chan = conn.channel()
138    chan.queue_declare( queue=amqp_config['queue'],
139                        durable=True,
140                        exclusive=True,
141                        auto_delete=False)
142    chan.exchange_declare( exchange=amqp_config['exchange'],
143                           type="direct",
144                           durable=True,
145                           auto_delete=False,)
146    chan.queue_bind( queue=amqp_config['queue'],
147                     exchange=amqp_config['exchange'],
148                     routing_key=amqp_config['routing_key'])
149
150    chan.basic_consume( queue=amqp_config['queue'],
151                        no_ack=True,
152                        callback=recv_callback,
153                        consumer_tag="testtag")
154    log.debug('Connected to rabbitmq server - '+amqp_config['host']+":"+amqp_config['port'])
155    while True:
156        chan.wait()
157    chan.basic_cancel("testtag")
158    chan.close()
159    conn.close()
160
161if __name__ == '__main__':
162    main()
163
164