--- a/backup.py Sat Jan 27 12:19:39 2018 +0000 +++ b/backup.py Sun Jan 28 00:11:18 2018 +0000 @@ -7,6 +7,7 @@ import time import borgend import repository +import sleep from enum import IntEnum from instance import BorgInstance from threading import Thread, Lock, Condition @@ -53,20 +54,19 @@ class Operation: CREATE='create' PRUNE='prune' - def __init__(self, operation, when_monotonic, **kwargs): + def __init__(self, operation, time, **kwargs): self.operation=operation - self.when_monotonic=when_monotonic + self.time=time self.detail=kwargs def when(self): - return self.when_monotonic-time.monotonic()+time.time() + return self.time.realtime() class Status(Operation): def __init__(self, backup, op=None): if op: - super().__init__(op.operation, op.when_monotonic, - **op.detail) + super().__init__(op.operation, op.time, **op.detail) else: super().__init__(None, None) @@ -137,6 +137,20 @@ 'Retry interval', loc, config.defaults['retry_interval']) + + scheduling=config.check_string(cfg, 'scheduling', + 'Scheduling mode', loc, + default="dreamtime") + + if scheduling=="dreamtime": + self.timeclass=sleep.DreamTime + elif scheduling=="realtime": + self.timeclass=sleep.MonotonicTime + elif scheduling=="manual": + self.backup_interval=0 + else: + logging.error("Invalid time class '%s' for %s" % (scheduling, loc)) + self.paths=config.check_nonempty_list_of_strings(cfg, 'paths', 'Paths', loc) self.borg_parameters=config.BorgParameters.from_config(cfg, loc) @@ -158,6 +172,7 @@ self.lastrun_finished=None self.state=State.INACTIVE self.errors=Errors.OK + self.timeclass=sleep.DreamTime self.__decode_config(cfg) @@ -285,7 +300,7 @@ self.current_operation=op # Update scheduled time to real starting time to schedule # next run relative to this - self.current_operation.when_monotonic=time.monotonic() + self.current_operation.time=sleep.MonotonicTime.now() self.state=State.ACTIVE # Reset error status when starting a new operation self.errors=Errors.OK @@ -354,7 +369,7 @@ self.errors=self.errors.combine(Errors.ERRORS) if self.current_operation.operation=='create': - self.lastrun_when=self.current_operation.when_monotonic + self.lastrun_when=self.current_operation.time.monotonic() self.lastrun_finished=time.monotonic() self.thread_res=None self.thread_log=None @@ -404,17 +419,17 @@ if not self.scheduled_operation: op=self.__next_operation_unlocked() if op: - now=time.monotonic() - delay=max(0, op.when_monotonic-now) - self.logger.info("Scheduling '%s' (detail: %s) in %d seconds" % - (str(op.operation), op.detail or 'none', delay)) + self.logger.info("Scheduling '%s' (detail: %s) in %d seconds [%s]" % + (str(op.operation), op.detail or 'none', + op.time.seconds_to(), + op.time.__class__.__name__)) self.scheduled_operation=op self.state=State.SCHEDULED self.__update_status() # Wait under scheduled wait - self.scheduler.wait_until(now+delay, self._cond, self.backup_name) + self.scheduler.wait_until(op.time, self._cond, self.backup_name) else: # Nothing scheduled - just wait self.logger.info("Waiting for manual scheduling") @@ -444,7 +459,6 @@ def __next_operation_unlocked(self): # TODO: pruning as well - now=time.monotonic() if not self.lastrun_finished: initial_interval=self.retry_interval if initial_interval==0: @@ -452,21 +466,20 @@ if initial_interval==0: return None else: - return Operation(Operation.CREATE, now+initial_interval, - reason='initial') + tm=self.timeclass.after(initial_interval) + return Operation(Operation.CREATE, tm, reason='initial') elif not self.errors.ok(): if self.retry_interval==0: return None else: - return Operation(Operation.CREATE, - self.lastrun_finished+self.retry_interval, - reason='retry') + tm=sleep.MonotonicTime(self.lastrun_finished+self.retry_interval) + return Operation(Operation.CREATE, tm, reason='retry') else: if self.backup_interval==0: return None else: - return Operation(Operation.CREATE, - self.lastrun_when+self.backup_interval) + tm=self.timeclass.from_monotonic(self.lastrun_when+self.backup_interval) + return Operation(Operation.CREATE, tm) def __status_unlocked(self): callback=self.__status_update_callback @@ -505,13 +518,13 @@ return res[0] def create(self): - op=Operation(Operation.CREATE, time.monotonic(), reason='manual') + op=Operation(Operation.CREATE, sleep.MonotonicTime.now(), reason='manual') with self._cond: self.scheduled_operation=op self._cond.notify() def prune(self): - op=Operation(Operation.PRUNE, time.monotonic(), reason='manual') + op=Operation(Operation.PRUNE, sleep.MonotonicTime.now(), reason='manual') with self._cond: self.scheduled_operation=op self._cond.notify()