def init(host='127.0.0.1', port=5672, usr='******', pas='******', channel=None, destinations=None):
    global channel_inst
    if not channel:
        # TODO should lazy init channel_inst
        channel_inst = connect_and_declare(host=host, port=port, usr=usr, pas=pas, destinations=destinations)
    else:
        channel_inst = channel

    bashtasks = BashTasks()
    bashtasks.post_task = post_task
    bashtasks.execute_task = execute_task
    return bashtasks
    def subscribe(self, callback, queue=TASK_RESPONSES_POOL):
        def pika_event_to_bashtasks_msg(ch, method, properties, body):
            msg = MessageAmqpPika(ch, method, properties, body)
            callback(msg)

        global channel_inst
        if not channel_inst:
            channel_inst = connect_and_declare(host=self.host, port=self.port, usr=self.usr, pas=self.pas)

        channel_inst.basic_qos(prefetch_count=1)  # consume msgs one at a time

        channel_inst.basic_consume(pika_event_to_bashtasks_msg, queue=queue, no_ack=False)

        channel_inst.start_consuming()
Exemple #3
0
def start_executor(host='127.0.0.1', port=5672, usr='******', pas='******', queue=DEFAULT_DESTINATION,
                   tasks_nr=1, max_retries=0, verbose=False, custom_callback=None,
                   ok_returncodes=(0, )):
    curr_th_name = threading.current_thread().name
    logger = get_logger(name=curr_module_name())
    logger.info(">> Starting executor %s connecting to rabbitmq: %s:%s@%s for executing %d tasks.",
                curr_th_name, usr, pas, host, tasks_nr)

    ch = connect_and_declare(host=host, port=port, usr=usr, pas=pas, destinations=queue)
    ch.basic_qos(prefetch_count=1)  # consume msgs one at a time

    channels.append(ch)

    def is_ok_returncode(code):
        return code in ok_returncodes

    def create_response_for(msg):
        resp = {}
        resp.update(msg)
        resp['executor_name'] = get_executor_name()
        resp['retries'] = msg.get('retries', 0)
        resp['max_retries'] = msg.get('max_retries', 0)
        return resp

    def should_retry(response_msg):
        is_error = response_msg['returncode'] != 0
        returncode = response_msg['returncode']
        is_retriable = returncode not in response_msg['non_retriable']
        current_retries = response_msg.get('retries', 0)
        msg_max_retries = response_msg.get('max_retries', max_retries)
        retries_pending = current_retries < msg_max_retries
        return is_error and is_retriable and retries_pending

    def send_response(response_msg):
        if should_retry(response_msg):
            logger.debug('---- retrying msg correlation_id: %d current_retries: %d of %d',
                         response_msg['correlation_id'], response_msg['retries'],
                         response_msg['max_retries'])
            tgt_exch = TASK_REQUESTS_POOL
            response_msg['retries'] += 1
        else:
            tgt_exch = TASK_RESPONSES_POOL

        response_str = json.dumps(response_msg)
        props = pika.BasicProperties(
                             delivery_mode=2,  # make message persistent
                          )
        ch.basic_publish(exchange=tgt_exch, routing_key='', body=response_str, properties=props)

    def tasks_nr_generator(tasks_nr):
        tasks_nr_gen = tasks_nr
        while True:
            tasks_nr_gen = tasks_nr_gen - 1 if tasks_nr_gen > 0 else tasks_nr_gen
            yield tasks_nr_gen

    def trace_msg(msg, context_info=''):
        logger.info(u'------------- MSG: %s', context_info)
        for key, value in msg.items():
            try:
                logger.info(u'\t%s:-> %s', key, value)
            except Exception as e:
                logger.error('error in trace message::::', exc_info=True)
                sys.exit(777)
        logger.info('---------------------------------------------')

    def execute_command(command):
        p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        o, e = p.communicate()

        return p.returncode, o.decode('utf-8'), e.decode('utf-8')

    def handle_message(ch, method, properties, body):
        msg = json.loads(body.decode('utf-8'))
        logger.debug(">>>> msg received: %s from queue %s : correlation_id %d command: %s",
                     curr_th_name, queue, msg['correlation_id'], msg['command'][:50])
        try:
            response_msg = create_response_for(msg)
            response_msg['pre_command_ts'] = currtimemillis()

            returncode, out, err = command_callback(msg['command'])

            response_msg['post_command_ts'] = currtimemillis()
            response_msg['returncode'] = returncode
            response_msg['stdout'] = out
            response_msg['stderr'] = err

        except Exception as exc:
            logger.error('**** Command execution error. Exception for : correlation_id %d ',
                         response_msg['correlation_id'], exc_info=True)
            response_msg['post_command_ts'] = currtimemillis()
            response_msg['returncode'] = -3791
            response_msg['stdout'] = 'Exception trying to execute command.'
            response_msg['stderr'] = repr(exc)
        finally:
            if verbose or not is_ok_returncode(response_msg['returncode']):
                trace_msg(msg, context_info='Request msg: '+str(msg['correlation_id']))
                resp_header = 'Response msg: '+str(response_msg['correlation_id']) + \
                              ' returncode: '+str(response_msg['returncode'])
                trace_msg(response_msg, context_info=resp_header)
            send_response(response_msg)
            ch.basic_ack(method.delivery_tag)

        tasks_nr_new_elem = next(tasks_nr_gen)

        logger.debug("<<<< executed by: executor %s correlation_id: %d pending: %d",
                     curr_th_name, response_msg['correlation_id'], tasks_nr_new_elem)

        if tasks_nr_new_elem == 0:
            logger.info('==== no more tasks to execute. Exiting.')
            stop_and_exit()

    tasks_nr_gen = tasks_nr_generator(tasks_nr)

    command_callback = custom_callback if custom_callback else execute_command

    ch.basic_consume(handle_message, queue=queue, no_ack=False)
    logger.info("<< Ready: executor %s connected to rabbitmq: %s:%s@%s",
                curr_th_name, usr, pas, host)
    ch.start_consuming()