def save(queue, limit, directory): """ Save messages from a queue to a file, non-destructively. """ mqstate.validate(['host', 'port', 'channel']) # Automatically generate a directory to save messages to if not directory: directory = safe_filename(mqstate.host + '_' + safe_filename(queue)) click.secho(f'Saving messages to \'{directory}\'...', dim=True, fg='green') # check that the directory is ready for use absolute_path = os.path.abspath(directory) if not os.path.exists(absolute_path): click.secho(f'Creating {absolute_path} to save messages in...', dim=True) os.makedirs(absolute_path, mode=0o755) click.secho(f'Saving a maximum of {limit} messages from {queue}...', dim=True) qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(), mqstate.username, mqstate.password) # https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.ref.dev.doc/q096780_.htm # https://github.com/dsuch/pymqi/blob/master/code/examples/put_get_correl_id.py gmo = pymqi.GMO() gmo.Options = pymqi.CMQC.MQGMO_BROWSE_NEXT queue = pymqi.Queue(qmgr, str(queue), pymqi.CMQC.MQOO_BROWSE) message_count = 0 while message_count < limit: try: request_md = pymqi.MD() message = queue.get(None, request_md, gmo) file_name = filename_from_attributes( mq_string(request_md.PutDate), mq_string(request_md.PutTime), '.' + hashlib.sha1(request_md.MsgId).hexdigest() + '.', request_md.MsgType, request_md.Expiry, mq_string(request_md.UserIdentifier), mq_string(request_md.Format), mq_string(request_md.PutApplName)) # try get a safe filename from all of that file_name = slugify(file_name) with open(os.path.join(absolute_path, file_name), 'wb') as f: f.write(message) click.secho(f'{message_count}: Wrote message to {file_name}', bold=True, fg='green') except pymqi.MQMIError as dme: if dme.comp == pymqi.CMQC.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_NO_MSG_AVAILABLE: click.secho('Dump complete. No more messages on the queue.', fg='yellow') break # if we are not allowed to GET on this queue, mention that and quit if dme.comp == pymqi.CMQ.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_GET_INHIBITED: click.secho('GET not allowed on queue with current access.', fg='red') break else: raise dme message_count += 1 click.secho(f'Saved {message_count} message(s) in total.', bold=True) queue.close() qmgr.disconnect()
def sniff(queue, store, directory): """ Sniff messages on a queue, non-destructively. Sniffs queues messages on a queue by opening the queue in a read only mode. Incoming messages will be dumped to the screen by default. If the --store flag is specified, messages will also be written to disk as they arrive. """ mqstate.validate(['host', 'port']) # check if a directory was set but store was not if directory and not store: click.secho('A directory was set to store messages but --store flag was not provided, ignoring...', bold=True, fg='yellow') # Prepare the destination directory if messages should also be saved if store: # Automatically generate a directory to save messages to if not directory: directory = safe_filename(mqstate.host + '_' + safe_filename(queue)) click.secho(f'Messages will be saved to directory \'{directory}\'', dim=True, fg='green') # check that the directory is ready for use absolute_path = os.path.abspath(directory) if not os.path.exists(absolute_path): click.secho(f'Creating {absolute_path} to save messages in...', dim=True) os.makedirs(absolute_path, mode=0o755) qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(), mqstate.username, mqstate.password) # https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.ref.dev.doc/q096780_.htm # https://github.com/dsuch/pymqi/blob/master/code/examples/put_get_correl_id.py # https://github.com/dsuch/pymqi/blob/master/code/examples/get_wait_multiple_messages.py gmo = pymqi.GMO() gmo.Options = pymqi.CMQC.MQGMO_BROWSE_NEXT | pymqi.CMQC.MQGMO_WAIT | pymqi.CMQC.MQGMO_FAIL_IF_QUIESCING gmo.WaitInterval = 2 * 1000 # 5 seconds queue = pymqi.Queue(qmgr, str(queue), pymqi.CMQC.MQOO_BROWSE) request_md = pymqi.MD() # simple counter for statistics message_count = 0 click.secho('Waiting for messages to arrive...', dim=True) while True: try: # grab the message message = queue.get(None, request_md, gmo) message_count += 1 # Save the message if we need to we do this early as # messages could be reformatted to be printed to screen. if store: file_name = filename_from_attributes( mq_string(request_md.PutDate), mq_string(request_md.PutTime), '.' + hashlib.sha1(request_md.MsgId).hexdigest() + '.', request_md.MsgType, request_md.Expiry, mq_string(request_md.UserIdentifier), mq_string(request_md.Format), mq_string(request_md.PutApplName)) # try get a safe filename from all of that file_name = slugify(file_name) with open(os.path.join(absolute_path, file_name), 'wb') as f: f.write(message) click.secho(f'{message_count}: Wrote message to {file_name}', bold=True, fg='green') # check if we have a MQSTR message. If we don't, try and filter # non-printables. if request_md.Format.strip() not in ['MQSTR', '']: # remove non-printables and update the Format # column with (stripped) so that it is visible message = message.decode('ascii', errors='ignore') request_md.Format = mq_string(request_md.Format) + ' (stripped)' click.secho(f'Message #{message_count}', fg='green') table = get_table_handle([ 'Date', 'Time', 'MsgID', 'MsgType', 'Expiry', 'User', 'Format', 'App Name'], markdown=False) table.append_row([ mq_string(request_md.PutDate), mq_string(request_md.PutTime), request_md.MsgId.decode('ascii', errors='ignore'), request_md.MsgType, request_md.Expiry, mq_string(request_md.UserIdentifier), mq_string(request_md.Format), mq_string(request_md.PutApplName), ]) # Print a 'header' for the message click.secho(table.get_string()) # Print the message itself click.secho('' + '*' * 40 + ' BEGIN MESSAGE DATA ' + '*' * 40, dim=True) click.secho(message, bold=True) click.secho('*' * 41 + ' END MESSAGE DATA ' + '*' * 41 + '\n', dim=True) # reset the request descriptor so we can reuse it for the next message. request_md.MsgId = pymqi.CMQC.MQMI_NONE request_md.CorrelId = pymqi.CMQC.MQCI_NONE request_md.GroupId = pymqi.CMQC.MQGI_NONE request_md.Format = pymqi.CMQC.MQGI_NONE except pymqi.MQMIError as dme: # No messages, that's OK, we can ignore it. if dme.comp == pymqi.CMQC.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_NO_MSG_AVAILABLE: continue # if we are not allowed to GET on this queue, mention that and quit if dme.comp == pymqi.CMQ.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_GET_INHIBITED: click.secho('GET not allowed on queue with current credentials.', fg='red') break else: # Some other error condition. raise dme except KeyboardInterrupt as _: click.secho('Stopping...', fg='yellow') break click.secho(f'\nSniffed {message_count} message(s) in total.', dim=True) queue.close() qmgr.disconnect()