class TemporaryRabbitMQ: server_config = """ [ {rabbit, [{tcp_listeners, [%(RABBITMQ_PORT)s]}]} ]. """ def __init__(self, configuration): self.started = False self.configuration = configuration self.client = None def start(self): # Choose some configuration values. self.dir = tempfile.mkdtemp() self.nodename = "paasmaker@localhost" self.pidfile = os.path.join(self.dir, self.nodename + '.pid') self.port = self.configuration.get_free_port() self.configfile = tempfile.mkstemp()[1] self.outputspoolfile = tempfile.mkstemp()[1] self.spoolfd = open(self.outputspoolfile, 'w') # Set up an environment. environment = {} environment['HOME'] = self.dir environment['RABBITMQ_BASE'] = self.dir environment['RABBITMQ_HOME'] = self.dir environment['RABBITMQ_MNESIA_BASE'] = self.dir environment['RABBITMQ_LOG_BASE'] = self.dir environment['RABBITMQ_NODENAME'] = self.nodename environment['RABBITMQ_PORT'] = str(self.port) environment['RABBITMQ_CONFIG_FILE'] = self.configfile # Set up a configuration file. (Needed to change the listening port) config = self.server_config % environment open(self.configfile + '.config', 'w').write(config) # Fire up the server. logging.info("Starting up rabbitmq server because requested by test.") self.process = subprocess.Popen([self.configuration.get_flat('rabbitmq_binary')], env=environment, stdout=self.spoolfd, stderr=self.spoolfd) # As this takes a while, wait until it's started. checkfd = open(self.outputspoolfile, 'r') max_times = 30 # 6 seconds. self.started = False while not self.started and max_times > 0: checkfd.seek(0) contents = checkfd.read() if contents.find("broker running") != -1: # It's started. self.started = True else: # Wait a bit longer. max_times -= 1 time.sleep(0.2) if not self.started: checkfd.seek(0) raise Exception("Failed to start RabbitMQ: " + checkfd.read()) def get_client(self, io_loop=None, callback=None): credentials = pika.PlainCredentials('guest', 'guest') # This will connect immediately. parameters = pika.ConnectionParameters(host='localhost', port=self.port, virtual_host='/', credentials=credentials) self.client = TornadoConnection(parameters, on_open_callback=callback, io_loop=io_loop) # TODO: This supresses some warnings during unit tests, but maybe is not good for production. self.client.set_backpressure_multiplier(1000) return self.client def stop(self): if self.started: if self.client: self.client.close() # Clean up the server. pid = int(open(self.pidfile, 'r').read()) os.kill(pid, signal.SIGTERM) os.unlink(self.configfile + '.config') os.unlink(self.outputspoolfile) shutil.rmtree(self.dir)
class ManagedRabbitMQ(ManagedDaemon): """ .. warning:: This class is not in a working state. Start a managed instance of a RabbitMQ server. No passwords or authentication details are set up on the new node. If you plan to use multiple nodes, give each one a different ``nodepurpose`` argument when calling ``configure()``. This prevents the RabbitMQ's from conflicting with each other. Please note that it can take 5-7 seconds to start the daemon. This was originally designed for unit tests, but later in development the code was rearranged to not require RabbitMQ, and as such this was no longer used. """ RABBITMQ_SERVER_CONFIG = """ [ {rabbit, [{tcp_listeners, [%(RABBITMQ_PORT)s]}]} ]. """ def configure(self, working_dir, port, bind_host, nodepurpose=None): """ Configure this instance. :arg str working_dir: The working directory. :arg int port: The port to listen on. :arg str bind_host: The address to bind to. :arg str|None nodepurpose: An optional string to append to the node name to make it unique. """ # TODO: Allow authentication. self.parameters['working_dir'] = working_dir self.parameters['port'] = port self.parameters['host'] = bind_host if nodepurpose: self.parameters['nodename'] = '%s-paasmaker@localhost' % nodepurpose else: self.parameters['nodename'] = 'paasmaker@localhost' self.parameters['pidfile'] = os.path.join(working_dir, self.parameters['nodename'] + '.pid') self.parameters['logfile'] = os.path.join(working_dir, 'rabbitmq.log') environment = {} environment['HOME'] = working_dir environment['RABBITMQ_BASE'] = working_dir environment['RABBITMQ_HOME'] = working_dir environment['RABBITMQ_MNESIA_BASE'] = working_dir environment['RABBITMQ_LOG_BASE'] = working_dir environment['RABBITMQ_NODENAME'] = self.parameters['nodename'] environment['RABBITMQ_PORT'] = str(self.parameters['port']) environment['RABBITMQ_CONFIG_FILE'] = self.get_configuration_path(working_dir) self.parameters['environment'] = environment # Create the working dir. If this fails, let it bubble up. if not os.path.exists(working_dir): os.makedirs(working_dir) self.save_parameters() def get_pid_path(self): return self.parameters['pidfile'] def is_running(self, keyword=None): return super(ManagedRabbitMQ, self).is_running('rabbitmq') def start(self, callback, error_callback): """ Start up the server for this instance. """ # Write out the configuration. configfile = self.get_configuration_path(self.parameters['working_dir']) rabbitconfig = self.RABBITMQ_SERVER_CONFIG % self.parameters['environment'] fp = open(configfile + '.config', 'w') fp.write(rabbitconfig) fp.close() logfp = open(self.parameters['logfile'], 'a') # Fire up the server. logging.info("Starting up RabbitMQ server on port %d (can take up to 10 seconds)." % self.parameters['port']) subprocess.Popen( [self.configuration.get_flat('rabbitmq_binary')], env=self.parameters['environment'], stdout=logfp, stderr=logfp ) def error(message): log_file = open(self.parameters['logfile'], 'r').read() error_callback(log_file) # Wait for the port to come into use. self.configuration.port_allocator.wait_until_port_used( self.configuration.io_loop, self.parameters['port'], 5, callback, error ) def get_client(self, callback=None): credentials = pika.PlainCredentials('guest', 'guest') # This will connect immediately. parameters = pika.ConnectionParameters(host=self.parameters['host'], port=self.parameters['port'], virtual_host='/', credentials=credentials) self.client = TornadoConnection(parameters, on_open_callback=callback, io_loop=self.configuration.io_loop) # TODO: This supresses some warnings during unit tests, but maybe is not good for production. self.client.set_backpressure_multiplier(1000) return self.client def stop(self, sig=signal.SIGTERM): """ Stop this instance of the rabbitmq server, allowing for it to be restarted later. """ if self.client: self.client.close() super(ManagedRabbitMQ, self).stop(sig) def destroy(self): """ Destroy this instance of rabbitmq, removing all assigned data. """ # Hard shutdown - we're about to delete the data anyway. self.stop(signal.SIGKILL) shutil.rmtree(self.parameters['working_dir'])