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 __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
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)