def test_pickle(mock_abc): # Create a controller, with a running job and some threads etc. conn = Controller() try: conn.machines = {"m": simple_machine("m", 1, 2)} job_id = conn.create_job(None, owner="me", keepalive=60.0) time.sleep(0.05) assert conn.get_job_state(None, job_id).state == JobState.ready assert mock_abc.running_theads == 2 finally: # Pickling the controller should succeed conn.stop() conn.join() assert mock_abc.running_theads == 0 pickled_conn = pickle.dumps(conn) del conn # Unpickling should succeed conn2 = pickle.loads(pickled_conn) try: # And some BMP connections should be running again assert mock_abc.running_theads == 2 # And our job should still be there assert conn2.get_job_state(None, job_id).state == JobState.ready finally: conn2.stop() conn2.join()
def conn(mock_abc, on_background_state_change): """Auto-stop a controller.""" conn = Controller(max_retired_jobs=2, on_background_state_change=on_background_state_change) try: yield conn finally: conn.stop() conn.join()
def __init__(self, config_filename, cold_start=False, port=22244): """ :param config_filename: \ The filename of the config file for the server which describes the\ machines to be controlled. :type config_filename: str :param cold_start: \ If False (the default), the server will attempt to restore its\ previous state, if True, the server will start from scratch. :type cold_start: bool :param port: Which port to listen on. Defaults to 22244. :type port: int """ # ============ STATE THAT NEEDS TO BE ALWAYS DEFINED ============ self._cold_start = cold_start self._port = port # Should the background thread terminate? self._stop = False # Flag for checking if the server is still alive self._running = False # Currently open sockets to clients. Once server started, should only # be accessed from the server thread. self._server_socket = None # The server core object that the object that is persisted self._controller = None # Buffered data received from each socket # {fd: buf, ...} self._client_buffers = {} # ============ SUPERCLASS INITIALISATION ============ PollingServerCore.__init__(self) ConfigurationReloader.__init__(self, config_filename, self.wake) # ============ ACTIVE OBJECTS ============ # The background thread in which the server will run self._server_thread = Thread(target=self._run, name="Server Thread") # The current server configuration options. Once server started, should # only be accessed from the server thread. self._configuration = Configuration() # Infer the saved-state location self._state_filename = self._get_state_filename( self.configuration_file) # Attempt to restore saved state if required if not self._cold_start and path.isfile(self._state_filename): try: with open(self._state_filename, "rb") as f: self._controller = unpickle(f) log.info("Server warm-starting from %s.", self._state_filename) except Exception: # Some other error occurred during unpickling. log.exception("Server state could not be unpacked from %s.", self._state_filename) self._controller = None # Perform cold-start if no saved state was loaded if self._controller is None: log.info("Server cold-starting.") self._controller = Controller() # Notify the background thread when something changes in the background # of the controller (e.g. power state changes). self._controller.on_background_state_change = self.wake # Read configuration file. This must succeed when the server is first # being started. if not self.read_config_file(): self._controller.stop() raise Exception("Config file could not be loaded.") # Start the server self._server_thread.start() self._running = True