Example #1
0
    def test_bindings(self):
        storage = self.storage

        # by default the storage is empty
        self.assertEquals(0, len(storage.list_bindings()))

        # add a binding to the storage
        storage.add_binding(self.local_folder, self.remote_folder)
        self.assertEquals(1, len(storage.list_bindings()))

        # reopen the storage from the disk and check that the binding is still
        # present
        storage = Storage(self.storage_folder)
        self.assertEquals(1, len(storage.list_bindings()))
Example #2
0
    def __init__(self, monitor=True, storage=None):
        self.monitor = monitor

        # where to persist the current bindings
        if storage is None or isinstance(storage, basestring):
            self.storage = Storage(storage)
        else:
            self.storage = storage
Example #3
0
    def test_states(self):
        storage = self.storage

        b = storage.add_binding(self.local_folder, self.remote_folder)
        self.assertEquals(0, len(storage.get_states(b)))

        # create a new state by looking for an unknown path
        s = storage.get_state(b, 'some_file.txt')
        self.assertEquals(s.local_state, 'unknown')
        s.set_state('local', 'created')

        # nothing is saved yet
        self.assertEquals(0, len(storage.get_states(b)))

        # save the new state
        storage.set_state(b, s)
        self.assertEquals(1, len(storage.get_states(b)))

        # open a new storage
        storage = Storage(self.storage_folder)
        self.assertEquals(1, len(storage.get_states(b)))
        s = storage.get_state(b, 'some_file.txt')
        self.assertEquals(s.local_state, 'created')

        # delete the state
        storage.delete_state(b, 'some_file.txt')

        # open a new storage and check that it's empty
        storage = Storage(self.storage_folder)
        self.assertEquals(0, len(storage.get_states(b)))
Example #4
0
class Controller(object):
    """Main public API for user level operations.

    Main operations:
      - create metadata store if missing
      - launch / stop synchronizer process
      - list attach local folder to remote folder
      - query synchronization states of elements
      - query the operation log
      - manually trigger refresh (sync operations)

    All operations performed by the controller are fast and asynchronous:
    the controller never waits for a remote network connection. All
    operations are queued in the shared store to be performed by the
    synchronizer process.
    """

    def __init__(self, monitor=True, storage=None):
        self.monitor = monitor

        # where to persist the current bindings
        if storage is None or isinstance(storage, basestring):
            self.storage = Storage(storage)
        else:
            self.storage = storage

        # TODO: check if synchronizer process is live

    def attach(self, local_folder, remote_folder, repository_url=None, username=None, password=None):
        self.storage.add_binding(
            local_folder, remote_folder, repository_url=repository_url, username=username, password=password
        )

    def list_bindings(self):
        return self.storage.list_bindings()

    def detach(self, local_folder):
        # TODO: implement me
        pass

    def split_path(self, local_path):
        """Return binding and relative path for a local absolute path

        If no matching binding is found raise ValueError
        """
        local_path = os.path.abspath(local_path)
        b = self.storage.get_binding(local_path)
        if b is None:
            raise ValueError("'%s' is not bound to any repository" % local_path)
        return b, local_path[len(b.local_folder) + 1 :]

    def status(self, local_folders=[]):
        """High level text status reflecting the local state"""
        summaries = {
            # regular cases
            ("unknown", "unknown"): "unknown",
            ("synchronized", "synchronized"): "synchronized",
            ("created", "unknown"): "locally_created",
            ("unknown", "created"): "remotely_created",
            ("modified", "synchronized"): "locally_modified",
            ("synchronized", "modified"): "remotely_modified",
            ("deleted", "synchronized"): "locally_deleted",
            ("synchronized", "deleted"): "remotely_deleted",
            ("deleted", "deleted"): "deleted",  # should never happen
            # conflict cases
            ("modified", "deleted"): "conflicted",
            ("deleted", "modified"): "conflicted",
            ("modified", "modified"): "conflicted",
        }
        results = []
        for binding, path in self.get_bindings_and_path_for(local_folders):
            cs = self.storage.get_state(binding, path)

            summary = summaries.get((cs.local_state, cs.remote_state))
            if summary is None:
                logging.warn("unexpected compound state: %s", cs)
                summary = "unknown"
            results.append((binding.local_folder, path, summary))
        return results

    def get_bindings_for(self, local_folders=[]):
        if not local_folders:
            return self.list_bindings()
        else:
            return [self.split_path(folder)[0] for folder in local_folders]

    def get_bindings_and_path_for(self, local_folders=[]):
        pairs = []
        if not local_folders:
            # exhaustive scan
            for b in self.storage.list_bindings():
                for p in sorted(self.storage.get_states(b).keys()):
                    pairs.append((b, p))
        else:
            for local_folder in local_folders:
                pairs.append(self.split_path(local_folder))
        return pairs

    def refresh(self, local_folders=[], async=True):
        bindings = self.get_bindings_for(local_folders)
        if async:
            # TODO queue a command to synchronizer process
            pass
        else:
            # perform synchronization right away
            for b in bindings:
                sync = Synchronizer(self.storage, binding=b)
                sync.update_local_info(updated_other=False)
                sync.update_remote_info(updated_other=True)