Example #1
0
    def __init__(self, config, callback):
        self.reconnection_delay = 1.0
        self.caller_callback = callback
        self.config = ConnectionConfig()
        self.config.read(config)
        self.callbacks = self.set_callbacks()

        credentials = PlainCredentials(**self.config.credentials)

        broker_config = self.config.broker_config
        broker_config['credentials'] = credentials

        parameters = ConnectionParameters(**broker_config)

        try:
            parameters.host = self.config.host
        except NoOptionError:
            pass

        try:
            parameters.heartbeat = int(self.config.heartbeat)
        except NoOptionError:
            pass

        try:
            parameters.heartbeat = int(self.config.heartbeat)
        except NoOptionError:
            pass

        self.connection = SelectConnection(parameters, self.on_connected)
Example #2
0
    def __init__(self,
                 config_path=None,
                 log=sys.stdout,
                 log_level=LogLevels.INFO):
        """
        Create an object to interface with the ARC server.

        :param config_path: Path to config JSON file, or ``None`` to use the default settings
        :param log:         File-like object to write log messages to, or ``None`` to disable
                            logging. Use ``sys.stdout`` or ``sys.stderr`` to print messages
                            (default: ``sys.stdout``).
        :param log_level:   The level of detail logs should show (default: `LogLevels.INFO`).
                            See `LogLevels` for the available levels

        :raises InvalidConfigError: if config is not valid JSON or is otherwise invalid
        """

        self.logger = arc.Logger(arc.Logger_getRootLogger(), "jobsubmit")
        # Add a log destination if the user has provided one
        if log:
            log_dest = arc.LogStream(log)
            log_dest.setFormat(arc.ShortFormat)
            arc.Logger_getRootLogger().addDestination(log_dest)
            arc.Logger_getRootLogger().setThreshold(log_level.value)

        config_dict = {}
        if config_path:
            try:
                self.logger.msg(
                    arc.DEBUG,
                    "Using jasmin_arc config: {}".format(config_path))
                # Let errors reading file bubble to calling code
                with open(config_path) as config_file:
                    config_dict = json.load(config_file)

            # Catch JSON parsing errors
            except ValueError as e:
                raise InvalidConfigError(e.message)

        self.config = ConnectionConfig(config_dict, logger=self.logger)

        # Create jinja2 environment for loading JSDL template(s)
        self.env = Environment(loader=PackageLoader(__name__, TEMPLATES_DIR),
                               autoescape=select_autoescape(["xml"]))

        self.cached_user_config = None
Example #3
0
class BrokerConnection(object):
    """Connection class which provides connection to AMQP broker."""

    def __init__(self, config, callback):
        self.reconnection_delay = 1.0
        self.caller_callback = callback
        self.config = ConnectionConfig()
        self.config.read(config)
        self.callbacks = self.set_callbacks()

        credentials = PlainCredentials(**self.config.credentials)

        broker_config = self.config.broker_config
        broker_config['credentials'] = credentials

        parameters = ConnectionParameters(**broker_config)

        try:
            parameters.host = self.config.host
        except NoOptionError:
            pass

        try:
            parameters.heartbeat = int(self.config.heartbeat)
        except NoOptionError:
            pass

        try:
            parameters.heartbeat = int(self.config.heartbeat)
        except NoOptionError:
            pass

        self.connection = SelectConnection(parameters, self.on_connected)

    def on_connected(self, connection):
        """Called when we're connected to AMQP broker."""
        logger.info("Connected to AMQP-broker.")
        connection.channel(self.on_channel_open)

    def on_channel_open(self, new_channel):
        """Called when channel has opened"""
        logger.info("Channel to AMQP-broker opened.")
        self.channel = new_channel
        self.channel.add_on_close_callback(self.on_channel_closed)
        self.generic_callback()

    def set_callbacks(self):
        """Set callbacks for queue factory."""

        self.exchange_callbacks = []
        self.queue_callbacks = []
        self.binding_callbacks = []

        for exchange in self.config.exchanges:
            tmp = {}
            tmp['exchange'] = exchange
            tmp.update(self.config.exchanges[exchange])
            self.exchange_callbacks.append(tmp)

        for queue in self.config.queues:
            tmp = {}
            tmp['queue'] = queue
            tmp.update(self.config.queues[queue])
            self.queue_callbacks.append(tmp)

        for binding in self.config.bindings:
            tmp = {}
            tmp['binding'] = binding
            tmp.update(self.config.bindings[binding])
            self.binding_callbacks.append(tmp)

    def generic_callback(self, channel=None, frame=None):
        """Create exchanges, queues and bindings."""

        if channel and frame:
            # You could do error handling here
            # or add code for anonymous queue handling
            pass

        if self.exchange_callbacks:
            config = self.exchange_callbacks.pop()
            config['exchange'] = config['exchange'].split(':', 1)[-1]
            logger.info("Creating exchange %s." % config['exchange'])
            config.update({"callback": self.generic_callback})
            self.channel.exchange_declare(**config)

        elif self.queue_callbacks:
            config = self.queue_callbacks.pop()
            config['queue'] = config['queue'].split(':', 1)[-1]
            logger.info("Creating queue %s." % config['queue'])
            config.update({"callback": self.generic_callback})
            self.channel.queue_declare(**config)

        elif self.binding_callbacks:
            config = self.binding_callbacks.pop()
            binding = config['binding'].split(':')
            del config['binding']
            config['exchange'] = binding[-1]
            config['queue'] = binding[1]
            logger.info("Creating binding (%s -> %s) with routing key %s" % (
                config['exchange'], config['queue'],
                config['routing_key']))
            config.update({"callback": self.generic_callback})
            self.channel.queue_bind(**config)

        else:
            logger.info("RabbitMQ exchanges, queues and bindings are now "
                        "configured.")
            self.caller_callback()

    def reset_reconnection_delay(self, *args):
        self.reconnection_delay = 1.0

    def loop(self):
        """Main loop"""

        while True:
            try:
                logger.info("Starting main loop")
                self.connection.add_on_open_callback(
                    self.reset_reconnection_delay)
                self.connection.ioloop.start()
            except socket.error as e:
                logger.error("Connection failed or closed unexpectedly: %s", e)
            except TypeError as e:
                logger.error("Connection failed or closed unexpectedly: %s", e)
            except KeyboardInterrupt:
                self.connection.close()
                self.connection.ioloop.start()
                break
            finally:
                self.reconnection_delay *= (random.random() * 0.5) + 1
                self.reconnection_delay = min(self.reconnection_delay, 60.0)
                logger.info("Trying reconnection in %s seconds",
                            self.reconnection_delay)
                time.sleep(self.reconnection_delay)

    def on_channel_closed(self, channel, code, text):
        logger.warning("Channel closed with reason '%s %s'",
                       code, text)
        self.connection.close(code, text)