def run(self): """ The main refresh state machine. Takes the conduit through the init->is_configured->refresh steps, setting its status at the appropriate time and performing nicely in the case of errors. """ log.debug("Started thread %s (thread: %s)" % (self, thread.get_ident())) try: log.debug("Refresh %s beginning" % self) self.cond.emit("sync-started") if not self.dataproviderWrapper.module.is_configured( isSource=self.cond.get_dataprovider_position( self.dataproviderWrapper)[0] == 0, isTwoWay=self.cond.is_two_way()): self.dataproviderWrapper.module.set_status( DataProvider.STATUS_DONE_SYNC_NOT_CONFIGURED) #Cannot continue if source not configured raise Exceptions.StopSync(self.state) self.state = self.REFRESH_STATE try: self.dataproviderWrapper.module.refresh() self.dataproviderWrapper.module.set_status( DataProvider.STATUS_DONE_REFRESH_OK) except Exceptions.RefreshError: self.dataproviderWrapper.module.set_status( DataProvider.STATUS_DONE_REFRESH_ERROR) log.warn("RefreshError: %s" % self.dataproviderWrapper) #Cannot continue with no source data raise Exceptions.StopSync(self.state) except Exception: self.dataproviderWrapper.module.set_status( DataProvider.STATUS_DONE_REFRESH_ERROR) log.critical( "UNKNOWN REFRESH ERROR: %s\n%s" % (self.dataproviderWrapper, traceback.format_exc())) #Cannot continue with no source data raise Exceptions.StopSync(self.state) except Exceptions.StopSync: log.warn("Sync Aborted") self.aborted = True conduit.GLOBALS.mappingDB.save() self.cond.emit("sync-completed", self.aborted, self.did_sync_error(), self.did_sync_conflict())
def check_thread_not_cancelled(self, dataprovidersToCancel): """ Checks if the thread has been scheduled to be cancelled. If it has then this function sets the status of the dataproviders to indicate that they were stopped through a cancel operation. """ if self.cancelled: for s in dataprovidersToCancel: s.module.set_status(DataProvider.STATUS_DONE_SYNC_CANCELLED) raise Exceptions.StopSync(self.state)
def run(self): """ The main syncronisation state machine. Takes the conduit through the refresh->get,put,get,put->done steps, setting its status at the appropriate time and performing nicely in the case of errors. It is also threaded so remember 1. Syncronization should not block the GUI 2. Due to pygtk/gtk single threadedness do not attempt to communicate with the gui in any way other than signals, which since Glib 2.10 are threadsafe. If any error occurs during sync raise a L{conduit.Exceptions.StopSync} exception otherwise exit normally @raise Exceptions.StopSync: Raises a L{conduit.Exceptions.StopSync} exception if the synchronisation state machine does not complete, in some way, without success. """ log.debug("Started thread %s (thread: %s)" % (self, thread.get_ident())) try: log.debug("Sync %s beginning. Slow: %s, Twoway: %s" % (self, self.cond.do_slow_sync(), self.cond.is_two_way())) #Variable to exit the loop finished = False #Keep track of those sinks that didnt refresh ok sinkDidntRefreshOK = {} sinkDidntConfigureOK = {} #Error handling is a bit complex because we need to send #signals back to the gui for display, and because some errors #are not fatal. If there is an error, set the #'working' statuses immediately (Sync, Refresh) and set the #Negative status (error, conflict, etc) at the end so they remain #on the GUI and the user can see them. #UNLESS the error is Fatal (causes us to throw a stopsync exceptiion) #in which case set the error status immediately. self.cond.emit("sync-started") while not finished: self.check_thread_not_cancelled([self.source] + self.sinks) log.debug("Syncworker state %s" % self.state) #Check dps have been configured if self.state is self.CONFIGURE_STATE: if not self.source.module.is_configured( isSource=True, isTwoWay=self.cond.is_two_way()): self.source.module.set_status( DataProvider.STATUS_DONE_SYNC_NOT_CONFIGURED) #Cannot continue if source not configured raise Exceptions.StopSync(self.state) for sink in self.sinks: if not sink.module.is_configured( isSource=False, isTwoWay=self.cond.is_two_way()): sinkDidntConfigureOK[sink] = True self.sinkErrors[ sink] = DataProvider.STATUS_DONE_SYNC_NOT_CONFIGURED #Need to have at least one successfully configured sink if len(sinkDidntConfigureOK) < len(self.sinks): #If this thread is a sync thread do a sync self.state = self.REFRESH_STATE else: #We are finished log.warn("Not enough configured datasinks") self.aborted = True self.state = self.DONE_STATE #refresh state elif self.state is self.REFRESH_STATE: log.debug("Source Status = %s" % self.source.module.get_status()) #Refresh the source try: self.source.module.refresh() self.source.module.set_status( DataProvider.STATUS_DONE_REFRESH_OK) except Exceptions.RefreshError: self.source.module.set_status( DataProvider.STATUS_DONE_REFRESH_ERROR) log.warn("RefreshError: %s" % self.source) #Cannot continue with no source data raise Exceptions.StopSync(self.state) except Exception: log.critical("UNKNOWN REFRESH ERROR: %s\n%s" % (self.source, traceback.format_exc())) self.source.module.set_status( DataProvider.STATUS_DONE_REFRESH_ERROR) #Cannot continue with no source data raise Exceptions.StopSync(self.state) #Refresh all the sinks. At least one must refresh successfully for sink in self.sinks: self.check_thread_not_cancelled([self.source, sink]) if sink not in sinkDidntConfigureOK: try: sink.module.refresh() sink.module.set_status( DataProvider.STATUS_DONE_REFRESH_OK) except Exceptions.RefreshError: log.warn("RefreshError: %s" % sink) sinkDidntRefreshOK[sink] = True self.sinkErrors[ sink] = DataProvider.STATUS_DONE_REFRESH_ERROR except Exception: log.critical("UNKNOWN REFRESH ERROR: %s\n%s" % (sink, traceback.format_exc())) sinkDidntRefreshOK[sink] = True self.sinkErrors[ sink] = DataProvider.STATUS_DONE_REFRESH_ERROR #Need to have at least one successfully refreshed sink if len(sinkDidntRefreshOK) < len(self.sinks): #If this thread is a sync thread do a sync if self.do_sync: self.state = self.SYNC_STATE else: #This must be a refresh thread so we are done self.state = self.DONE_STATE else: #We are finished log.info("Not enough sinks refreshed") self.aborted = True self.state = self.DONE_STATE #synchronize state elif self.state is self.SYNC_STATE: for sink in self.sinks: self.check_thread_not_cancelled([self.source, sink]) #only sync with those sinks that refresh'd OK if sink not in sinkDidntRefreshOK: try: #now perform a one or two way sync depending on the user prefs #and the capabilities of the dataprovider if self.cond.is_two_way(): #two way self.two_way_sync(self.source, sink) else: #one way self.one_way_sync(self.source, sink) except Exceptions.SyncronizeFatalError, err: log.warn("%s\n%s" % (err, traceback.format_exc())) sink.module.set_status( DataProvider.STATUS_DONE_SYNC_ERROR) self.source.module.set_status( DataProvider.STATUS_DONE_SYNC_ERROR) #cannot continue with this source, sink pair continue except Exception: log.critical( "UNKNOWN SYNCHRONIZATION ERROR\n%s" % traceback.format_exc()) sink.module.set_status( DataProvider.STATUS_DONE_SYNC_ERROR) self.source.module.set_status( DataProvider.STATUS_DONE_SYNC_ERROR) #cannot continue with this source, sink pair continue