borgend/backup.py

changeset 104
d33e2d7dbeb1
parent 100
b141bed9e718
child 106
a7bdc239ef62
equal deleted inserted replaced
103:32f2154ef25e 104:d33e2d7dbeb1
32 PAUSED=1 32 PAUSED=1
33 SCHEDULED=2 33 SCHEDULED=2
34 QUEUED=3 34 QUEUED=3
35 ACTIVE=4 35 ACTIVE=4
36 36
37 def __str__(self):
38 return _statestring[self]
37 39
38 class Errors(IntEnum): 40 class Errors(IntEnum):
39 OK=0 41 OK=0
40 BUSY=1 42 BUSY=1
41 OFFLINE=2 43 OFFLINE=2
55 Errors.BUSY: 'busy', 57 Errors.BUSY: 'busy',
56 Errors.OFFLINE: 'offline', 58 Errors.OFFLINE: 'offline',
57 Errors.ERRORS: 'errors' 59 Errors.ERRORS: 'errors'
58 } 60 }
59 61
62 _statestring={
63 State.INACTIVE: 'inactive',
64 State.PAUSED: 'paused',
65 State.SCHEDULED: 'scheduled',
66 State.QUEUED: 'queued',
67 State.ACTIVE: 'active'
68 }
69
60 class Operation: 70 class Operation:
61 CREATE='create' 71 CREATE='create'
62 PRUNE='prune' 72 PRUNE='prune'
63 INFO='info' 73 INFO='info'
64 LIST='list' 74 LIST='list'
76 def ok(self): 86 def ok(self):
77 return self.errors.ok() 87 return self.errors.ok()
78 88
79 def add_error(self, error): 89 def add_error(self, error):
80 self.errors=self.errors.combine(error) 90 self.errors=self.errors.combine(error)
91
92 def name(self):
93 if 'reason' in self.detail:
94 return str(self.type) + '.' + self.detail['reason']
95 else:
96 return str(self.type)
81 97
82 98
83 class Status(Operation): 99 class Status(Operation):
84 def __init__(self, backup, op=None): 100 def __init__(self, backup, op=None):
85 op=None 101 op=None
445 self.borg_instance=inst 461 self.borg_instance=inst
446 self.current_operation=op 462 self.current_operation=op
447 # Update scheduled time to real starting time to schedule 463 # Update scheduled time to real starting time to schedule
448 # next run relative to this 464 # next run relative to this
449 self.current_operation.start_time=MonotonicTime.now() 465 self.current_operation.start_time=MonotonicTime.now()
450 self.state=State.ACTIVE
451 # Reset error status when starting a new operation 466 # Reset error status when starting a new operation
452 self.__update_status() 467 self.__update_status(State.ACTIVE)
453 468
454 t_log.start() 469 t_log.start()
455 t_res.start() 470 t_res.start()
456 471
457 472
575 def __main_thread_wait_schedule(self): 590 def __main_thread_wait_schedule(self):
576 op=None 591 op=None
577 if self._pause: 592 if self._pause:
578 self.logger.info("Waiting for resume to be signalled") 593 self.logger.info("Waiting for resume to be signalled")
579 594
580 self.state=State.PAUSED 595 self.__update_status(State.PAUSED)
581 self.__update_status()
582 596
583 self._cond.wait() 597 self._cond.wait()
584 else: 598 else:
585 if not self.scheduled_operation: 599 if not self.scheduled_operation:
586 op=self.__next_operation_unlocked() 600 op=self.__next_operation_unlocked()
587 if op: 601 if op:
588 self.logger.info("Scheduling '%s' (detail: %s) on %s [%s]" %
589 (str(op.type), op.detail or 'none',
590 op.start_time.isoformat(),
591 op.start_time.__class__.__name__))
592
593 self.scheduled_operation=op 602 self.scheduled_operation=op
594 self.state=State.SCHEDULED 603
595 self.__update_status() 604 self.__update_status(State.SCHEDULED)
596 605
597 # Wait under scheduled wait 606 # Wait under scheduled wait
598 self.scheduler.wait_until(op.start_time, self._cond, self.backup_name) 607 eventname=op.name() + '@' + self.backup_name
608 self.scheduler.wait_until(op.start_time, self._cond, eventname)
599 else: 609 else:
600 # Nothing scheduled - just wait 610 # Nothing scheduled - just wait
601 self.logger.info("Waiting for manual scheduling") 611 self.logger.info("Waiting for manual scheduling")
602 612
603 self.state=State.INACTIVE 613 self.__update_status(State.INACTIVE)
604 self.__update_status()
605 614
606 self._cond.wait() 615 self._cond.wait()
607 616
608 # Main thread/3. If there is a scheduled operation (it might have been 617 # Main thread/3. If there is a scheduled operation (it might have been
609 # changed manually from 'op' created in __main_thread_wait_schedule above), 618 # changed manually from 'op' created in __main_thread_wait_schedule above),
610 # queue it on the repository, and launch the operation once repository 619 # queue it on the repository, and launch the operation once repository
611 # available 620 # available
612 def __main_thread_queue_and_launch(self): 621 def __main_thread_queue_and_launch(self):
613 if self.scheduled_operation: 622 if self.scheduled_operation:
614 self.logger.debug("Queuing") 623
615 self.state=State.QUEUED 624 self.__update_status(State.QUEUED)
616 self.__update_status() 625
617 res=self.repository.queue_action(self._cond, 626 res=self.repository.queue_action(self._cond,
618 action=self.__launch_and_wait, 627 action=self.__launch_and_wait,
619 name=self.backup_name) 628 name=self.backup_name)
620 if not res: 629 if not res:
621 self.logger.debug("Queueing aborted") 630 self.logger.debug("Queueing aborted")
709 callback=self.__status_update_callback 718 callback=self.__status_update_callback
710 status=Status(self) 719 status=Status(self)
711 720
712 return status, callback 721 return status, callback
713 722
714 def __update_status(self): 723 def __update_status(self, state):
724 self.logger.debug("Entering %s state", str(state))
725 self.state=state
715 status, callback = self.__status_unlocked() 726 status, callback = self.__status_unlocked()
716 if callback: 727 if callback:
717 #self._cond.release() 728 #self._cond.release()
718 try: 729 try:
719 callback(status) 730 callback(status)
753 if self.borg_instance: 764 if self.borg_instance:
754 self.borg_instance.terminate() 765 self.borg_instance.terminate()
755 766
756 def is_paused(self): 767 def is_paused(self):
757 with self._cond: 768 with self._cond:
758 paused=self.state==State.PAUSED 769 paused=(self.state==State.PAUSED)
759 return paused 770 return paused
760 771
761 def pause(self): 772 def pause(self):
762 with self._cond: 773 with self._cond:
763 self.logger.debug('Pause signalled') 774 self.logger.debug('Pause signalled')

mercurial