borgend/repository.py

changeset 80
a409242121d5
parent 79
b075b3db3044
child 86
2fe66644c50d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/borgend/repository.py	Sun Jan 28 11:54:46 2018 +0000
@@ -0,0 +1,143 @@
+#
+# Repository abstraction for queuing
+#
+
+import weakref
+import keyring
+
+from . import loggers
+from . import config
+from .scheduler import QueueThread, QueuedEvent
+
+logger=loggers.get(__name__)
+
+class FIFOEvent(QueuedEvent):
+    def __init__(self, cond, name=None):
+        self._goodtogo=False
+        super().__init__(cond, name=name)
+
+    def __lt__(self, other):
+        return False
+
+class FIFO(QueueThread):
+    def __init__(self, **kwargs):
+        super().__init__(target = self._fifo_thread, **kwargs)
+
+    def _fifo_thread(self):
+        with self._cond:
+            while not self._terminate:
+                ev=self._list
+                if ev:
+                    # We can only remove ev from the list when ev.cond allows
+                    with ev.cond:
+                        if not ev._goodtogo:
+                            ev._goodtogo=True
+                            ev.cond.notifyAll()
+                self._cond.wait()
+
+            # Termination cleanup
+            ev=self._list
+            while ev:
+                # We can only remove ev from the list when ev.cond allows
+                with ev.cond:
+                    ev.cond.notifyAll()
+                    ev=ev.next
+
+    # cond has to be acquired on entry!
+    def queue_action(self, cond, action=lambda: (), name=None):
+        ev=FIFOEvent(cond, name=name)
+
+        with self._cond:
+            self._insert(ev)
+
+        # This will release the lock on cond, allowing queue manager (scheduler)
+        # thread to notify us if we are already to be released
+        logger.debug("%s:Queuing %s", self.name, ev.name or 'UNKNOWN')
+        ev.cond.wait()
+
+        try:
+            if ev._goodtogo:
+                logger.debug("%s:Executing %s", self.name, ev.name or 'UNKNOWN')
+                #
+                # TODO: action() has to unlink on finish; so should maybe
+                # use weak references to event.
+                # Or we have to make action take all the time, so make the
+                # stdout thread.
+                # OR: Easiest to just move finish-waiting into __launch_check
+                # instead of at the outer level of the main loop.
+                #
+                action()
+        finally:
+            with self._cond:
+                self._unlink(ev)
+                # Let _fifo_thread proceed to next action
+                self._cond.notify()
+
+        return ev._goodtogo
+
+repositories=weakref.WeakValueDictionary()
+
+class Repository(FIFO):
+    def __decode_config(self, cfg):
+        loc0='Repository %d' % self.identifier
+
+        self.repository_name=config.check_string(cfg, 'name', 'Name', loc0)
+
+        logger.debug("Configuring repository '%s'" % self.repository_name)
+
+        loc = 'Repository "%s"'
+
+        self.logger=logger.getChild(self.repository_name)
+
+        self.location=config.check_string(cfg, 'location',
+                                         'Target repository location', loc)
+
+        self.borg_parameters=config.BorgParameters.from_config(cfg, loc)
+
+        self.__keychain_account=config.check_string(cfg, 'keychain_account',
+                                                    'Keychain account', loc,
+                                                    default='')
+
+        self.__passphrase=None
+
+        if config.settings['extract_passphrases_at_startup']:
+            try:
+                self.extract_passphrase()
+            except Exception:
+                pass
+
+    def __init__(self, identifier, cfg):
+        self.identifier=identifier
+        self.__decode_config(cfg)
+        super().__init__(name = 'RepositoryThread %s' % self.repository_name)
+        repositories[self.repository_name]=self
+
+    def __extract_passphrase(self):
+        acc=self.__keychain_account
+        if not self.__passphrase:
+            if acc and acc!='':
+                self.logger.debug('Requesting passphrase')
+                try:
+                    pw=keyring.get_password("borg-backup", acc)
+                except Exception as err:
+                    self.logger.error('Failed to retrieve passphrase')
+                    raise err
+                else:
+                    self.logger.debug('Received passphrase')
+                self.__passphrase=pw
+            else:
+                self.__passphrase=None
+        return self.__passphrase
+
+    def launch_borg_instance(self, inst):
+        with self._cond:
+            passphrase=self.__extract_passphrase()
+        inst.launch(passphrase=passphrase)
+
+def find_repository(name):
+    if name in repositories:
+        return repositories[name]
+    else:
+        return None
+
+

mercurial