예제 #1
0
    def scan(self) -> List[SystemFile]:
        if self.__first_run:
            self._install_scanfs()
            self.__first_run = False

        retries = 0
        out = None
        while out is None:
            try:
                out = self.__ssh.shell("'{}' '{}'".format(
                    self.__remote_path_to_scan_script,
                    self.__remote_path_to_scan))
            except SshcpError as e:
                # Suppress specific errors and retry a fixed number of times
                # Otherwise raise a fatal AppError
                if RemoteScanner.__suppress_error(
                        e) and retries < RemoteScanner.RETRY_COUNT:
                    self.logger.warning(
                        "Retrying remote scan after error: {}".format(str(e)))
                    out = None
                    retries += 1
                else:
                    self.logger.exception("Caught an SshError")
                    raise AppError(Localization.Error.REMOTE_SERVER_SCAN)

        try:
            remote_files = pickle.loads(out)
        except pickle.UnpicklingError as err:
            self.logger.error("Unpickling error: {}\n{}".format(str(err), out))
            raise AppError(Localization.Error.REMOTE_SERVER_SCAN)
        return remote_files
예제 #2
0
 def scan(self) -> List[SystemFile]:
     try:
         result = self.__scanner.scan()
     except SystemScannerError:
         self.logger.exception("Caught SystemScannerError")
         raise AppError(Localization.Error.LOCAL_SERVER_SCAN)
     return result
예제 #3
0
 def _install_scanfs(self):
     self.logger.info("Installing local:{} to remote:{}".format(
         self.__local_path_to_scan_script,
         self.__remote_path_to_scan_script))
     if not os.path.isfile(self.__local_path_to_scan_script):
         raise RemoteScannerError(
             "Failed to find scanfs executable at {}".format(
                 self.__local_path_to_scan_script))
     try:
         self.__scp.copy(local_path=self.__local_path_to_scan_script,
                         remote_path=self.__remote_path_to_scan_script)
     except ScpError:
         self.logger.exception("Caught scp exception")
         raise AppError(Localization.Error.REMOTE_SERVER_INSTALL)
예제 #4
0
    def run(self):
        self.context.logger.info("Starting SeedSync")
        self.context.logger.info("Platform: {}".format(platform.machine()))

        # Create controller
        controller = Controller(self.context, self.controller_persist)

        # Create auto queue
        auto_queue = AutoQueue(self.context, self.auto_queue_persist,
                               controller)

        # Create web app
        web_app_builder = WebAppBuilder(self.context, controller,
                                        self.auto_queue_persist)
        web_app = web_app_builder.build()

        # Define child threads
        controller_job = ControllerJob(
            context=self.context.create_child_context(ControllerJob.__name__),
            controller=controller,
            auto_queue=auto_queue)
        webapp_job = WebAppJob(context=self.context.create_child_context(
            WebAppJob.__name__),
                               web_app=web_app)

        do_start_controller = True

        # Initial checks to see if we should bother starting the controller
        if Seedsync._detect_incomplete_config(self.context.config):
            if not self.context.args.exit:
                do_start_controller = False
                self.context.logger.error("Config is incomplete")
                self.context.status.server.up = False
                self.context.status.server.error_msg = Localization.Error.SETTINGS_INCOMPLETE
            else:
                raise AppError("Config is incomplete")

        # Start child threads here
        if do_start_controller:
            controller_job.start()
        webapp_job.start()

        try:
            prev_persist_timestamp = datetime.now()

            # Thread loop
            while True:
                # Persist to file occasionally
                now = datetime.now()
                if (now - prev_persist_timestamp).total_seconds(
                ) > Constants.MIN_PERSIST_TO_FILE_INTERVAL_IN_SECS:
                    prev_persist_timestamp = now
                    self.persist()

                # Propagate exceptions
                webapp_job.propagate_exception()
                # Catch controller exceptions and keep running, but notify the web server of the error
                try:
                    controller_job.propagate_exception()
                except AppError as exc:
                    if not self.context.args.exit:
                        self.context.status.server.up = False
                        self.context.status.server.error_msg = str(exc)
                        Seedsync.logger.exception("Caught exception")
                    else:
                        raise

                # Check if a restart is requested
                if web_app_builder.server_handler.is_restart_requested():
                    raise ServiceRestart()

                # Nothing else to do
                time.sleep(Constants.MAIN_THREAD_SLEEP_INTERVAL_IN_SECS)

        except Exception:
            self.context.logger.info("Exiting Seedsync")

            # This sleep is important to allow the jobs to finish setup before we terminate them
            # If we kill too early, the jobs may leave lingering threads around
            # Note: There might be a better way to ensure that job setup has completed, but this
            #       will do for now
            time.sleep(Constants.MAIN_THREAD_SLEEP_INTERVAL_IN_SECS)

            # Join all the threads here
            if do_start_controller:
                controller_job.terminate()
            webapp_job.terminate()

            # Wait for the threads to close
            if do_start_controller:
                controller_job.join()
            webapp_job.join()

            # Last persist
            self.persist()

            # Raise any exceptions so they can be logged properly
            # Note: ServiceRestart and ServiceExit will be caught and handled
            #       by outer code
            raise